「第一回 オフラインリアルタイムどう書く」へなちょこ解答(Java)

金曜の夜に「第一回 オフラインリアルタイムどう書く」へ行ってきました。出題された問題を好きなプログラミング言語で好きなように書いて解く会です。

問題は、鍋谷さんが作成してくれました。今回は三目並べです!

1時間かけて各自問題を解き、その後、書いたコードを順番に発表しました。言語は、C1名、Java2名、Perl1名、Ruby2名だったと思います(鍋谷さんによる解答例の説明もあり)。

とても楽しかったです。ありがとうございました(>鍋谷さん、参加された皆さん)

自分は残念ながら時間内に完成しなかったので、動いていないコードを人前で説明する恥ずかしさと悔しさを味わうことができました。ひー。

あらためて家で解き直したので、自分の解答を載せておきます。Javaです。

class Board {

	int[][] board;

	public Board(int[] input) {
		board = new int[getRowMax()][getColMax()];
		for (int i = 0; i < getRowMax(); i++) {
			for (int j = 0; j < getColMax(); j++) {
				board[i][j] = 0;
			}
		}
	}

	protected int getRowMax() {
		return 3;
	}

	protected int getColMax() {
		return 3;
	}

	protected int getRowNum(int i) {
		return (i - 1) / getRowMax();
	}

	protected int getColNum(int i) {
		return i % getColMax();
	}
}

/**
 * 問題: http://nabetani.sakura.ne.jp/hena/1/
 * 
 */
public class TicTacToe {

	protected String execute(String string) {

		char[] charArray = string.toCharArray();

		int[] input = new int[charArray.length];
		int inputCounter = 0;
		for (int i = 0; i < charArray.length; i++) {
			input[inputCounter] = Integer.valueOf(String.valueOf(charArray[i]));
			inputCounter++;
		}

		Board b = new Board(input);
		int flag = -1;
		int player = 0;
		for (int i = 0; i < input.length && i < b.getColMax() * b.getColMax(); i++) {

			// 先攻o == 1, 後攻x == 2
			player = i % 2 + 1;

			int hand = input[i];
			int tmp = b.board[b.getRowNum(hand)][b.getColNum(hand)];

			if (tmp != 0) {
				// すでに○×を記入済みの位置であればFoul
				flag = 0;
				break;
			} else {
				// 未記入であれば、記入したプレイヤーを記録する
				b.board[b.getRowNum(hand)][b.getColNum(hand)] = player;

				// 今回の手番で埋められた一列がないかを確認する
				if (checkRow(b, player, input, hand)
						|| checkCol(b, player, input, hand)
						|| checkDiagonalLeft(b, player, input, hand)
						|| checkDiagonalRight(b, player, input, hand)) {
					flag = 1;
					break;
				}
			}
		}

		return getResultString(player, flag);
	}

	protected String getResultString(int player, int flag) {
		String result = null;

		if (flag == 0) {
			// ファール
			String winner = null;
			if (player == 1) {
				winner = "x";
			} else if (player == 2) {
				winner = "o";
			}
			result = "Foul : " + winner + " won.";
		} else if (flag == 1) {
			// 勝負アリ
			String winner = null;
			if (player == 1) {
				winner = "o";
			} else if (player == 2) {
				winner = "x";
			}
			result = winner + " won.";
		} else if (flag == -1) {
			// 引き分け
			result = "Draw game.";
		}
		return result;
	}

	/**
	 * 行をチェックする。(例)123, 456, 789
	 * 
	 * @return trueなら一列を形成済み
	 */
	protected boolean checkRow(Board b, int player, int[] input, int hand) {
		boolean result = false;
		int counter = 0;
		for (int j = 0; j < b.getColMax(); j++) {
			int cmp = b.board[b.getRowNum(hand)][j];
			if (cmp == player) {
				counter++;
			}
		}
		if (counter == b.getColMax()) {
			result = true;
		}
		return result;
	}

	/**
	 * 列をチェックする。(例)147, 258, 369
	 * 
	 * @return trueなら一列を形成済み
	 */
	protected boolean checkCol(Board b, int player, int[] input, int hand) {
		boolean result = false;
		int counter = 0;
		for (int j = 0; j < b.getRowMax(); j++) {
			int cmp = b.board[j][b.getColNum(hand)];
			if (cmp == player) {
				counter++;
			}
		}
		if (counter == b.getColMax()) {
			result = true;
		}
		return result;
	}

	/**
	 * 左上から右下の斜め一列をチェックする。 (例)1, 5, 9 : 1, 1 + (3 + 1), 5 + (3 + 1)
	 * 
	 * @return trueなら一列を形成済み
	 */
	protected boolean checkDiagonalLeft(Board b, int player, int[] input,
			int hand) {
		boolean result = false;
		int counter = 0;
		for (int j = 1; j < b.getColMax() * b.getRowMax() + 1; j += (b
				.getColMax() + 1)) {
			int cmp = b.board[b.getRowNum(j)][b.getColNum(j)];
			if (cmp == player) {
				counter++;
			}
		}
		if (counter == b.getColMax()) {
			result = true;
		}
		return result;
	}

	/**
	 * 右上から左下の斜め一列をチェックする。 (例)3, 5, 7: 3, 3 + (3 - 1), 5 + (3 - 1)
	 * 
	 * @return trueなら一列を形成済み
	 */
	protected boolean checkDiagonalRight(Board b, int player, int[] input,
			int hand) {
		boolean result = false;
		int counter = 0;
		for (int j = b.getColMax(); j < b.getColMax() * b.getRowMax(); j += (b
				.getColMax() - 1)) {
			int cmp = b.board[b.getRowNum(j)][b.getColNum(j)];
			if (cmp == player) {
				counter++;
			}
		}
		if (counter == b.getColMax()) {
			result = true;
		}
		return result;
	}

	public static void main(String[] args) {
		TicTacToe ttt = new TicTacToe();
		ttt.execute("5439126787");
		// すべてのテストデータは、TicTacToeTest.java(https://gist.github.com/3075622)を参照。
	}
}

なぜこうも長いのか……。

intの二次元配列で盤面を表わします。配列要素の初期状態は全マス0です。プレイヤーが記入すると、プレイヤーを表わすint値を配列要素として格納します。

あとは、判定ですね。

  • すでに配列要素が0以外であれば、記入済みのところに書こうとしたということでファールにします。
  • ファールでなければ、その手番に、すべて○あるいはすべて×の一列がタテ・ヨコ・ナナメのどこかで生成されていないかを調べます。生成されていたら、その手番で勝負がついたとみなします。
  • 最後まで勝負がつかなければ、引き分けとします。

そんなかんじです。ビット演算は未トライです。