Last active
January 6, 2022 02:09
-
-
Save MikuroXina/ecae87d9e96fe9ac8ac160e3d4bd78cb to your computer and use it in GitHub Desktop.
The Connected Four game.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Diagnostics; | |
| using System.Linq; | |
| using System.Text.RegularExpressions; | |
| using Internal; | |
| enum Cell { | |
| Empty, | |
| Black, | |
| White, | |
| } | |
| class Board { | |
| Cell[, ] cells; | |
| Action<Player> onGameSet; | |
| public Board(int width, int height, Action<Player> gameSet) { | |
| cells = new Cell[width, height]; | |
| onGameSet = gameSet; | |
| for (int y = 0; y < cells.GetLength(1); ++y) { | |
| for (int x = 0; x < cells.GetLength(0); ++x) { | |
| cells[x, y] = Cell.Empty; | |
| } | |
| } | |
| } | |
| Player PlayerFrom(Cell cell) { | |
| if (cell == Cell.Black) { | |
| return Player.Black; | |
| } | |
| if (cell == Cell.White) { | |
| return Player.White; | |
| } | |
| throw new Exception("Illegal cell state"); | |
| } | |
| int CountSeq(Cell target, int x, int y, int vx, int vy) { | |
| var looking = GetCell(x + vx, y + vy); | |
| if (looking != target) { | |
| return 0; | |
| } | |
| return 1 + CountSeq(target, x + vx, y + vy, vx, vy); | |
| } | |
| void CheckCellsFilled() { | |
| for (int y = 0; y < cells.GetLength(1); ++y) { | |
| for (int x = 0; x < cells.GetLength(0); ++x) { | |
| if (cells[x, y] == Cell.Empty) { | |
| return; | |
| } | |
| } | |
| } | |
| onGameSet(Player.Draw); | |
| } | |
| void Judge(int x, int y) { | |
| var current = GetCell(x, y); | |
| if (current == Cell.Empty) { return; } | |
| var xDirCount = CountSeq(current, x, y, 1, 0) + CountSeq(current, x, y, -1, 0); | |
| var yDirCount = CountSeq(current, x, y, 0, 1) + CountSeq(current, x, y, 0, -1); | |
| var xyDirCount = CountSeq(current, x, y, 1, 1) + CountSeq(current, x, y, -1, -1); | |
| var x_yDirCount = CountSeq(current, x, y, 1, -1) + CountSeq(current, x, y, -1, 1); | |
| if (3 <= xDirCount || 3 <= yDirCount || 3 <= xyDirCount || 3 <= x_yDirCount) { | |
| onGameSet(PlayerFrom(current)); | |
| } | |
| CheckCellsFilled(); | |
| } | |
| public Cell GetCell(int x, int y) { | |
| if (!(0 <= x && x < cells.GetLength(0) && | |
| 0 <= y && y < cells.GetLength(1))) { | |
| return Cell.Empty; | |
| } | |
| return cells[x, y]; | |
| } | |
| public void FallCell(Cell cell, int x) { | |
| if (!(0 <= x && x < cells.GetLength(0))) { | |
| throw new ArgumentOutOfRangeException(""); | |
| } | |
| for (int y = 0; y < cells.GetLength(1); ++y) { | |
| if (cells[x, y] == Cell.Empty) { | |
| cells[x, y] = cell; | |
| Judge(x, y); | |
| return; | |
| } | |
| } | |
| throw new OverflowException(""); | |
| } | |
| } | |
| enum Player { | |
| Black, | |
| White, | |
| Draw, | |
| } | |
| class GameView { | |
| Board board; | |
| bool playing; | |
| Player player; | |
| readonly int W; | |
| readonly int H; | |
| public GameView(int width, int height) { | |
| board = new Board(width, height, this.GameSet); | |
| player = Player.Black; | |
| W = width; | |
| H = height; | |
| } | |
| void GameSet(Player winner) { | |
| Flush(); | |
| if (winner == Player.Draw) { | |
| Console.WriteLine("引き分け!"); | |
| } else { | |
| Console.WriteLine("{0}の勝利!", PlayerName()); | |
| } | |
| playing = false; | |
| } | |
| void StartGame() { | |
| playing = true; | |
| } | |
| void Flush() { | |
| Console.Clear(); | |
| // Positions | |
| for (int x = 0; x < W; ++x) { | |
| Console.Write(" {0}", x); | |
| } | |
| Console.WriteLine(); | |
| // Cells | |
| for (int y = H - 1; 0 <= y; --y) { | |
| for (int x = 0; x < W; ++x) { | |
| var cellChar = "_"; | |
| switch (board.GetCell(x, y)) { | |
| case Cell.Black: | |
| cellChar = "■"; | |
| break; | |
| case Cell.White: | |
| cellChar = "○"; | |
| break; | |
| } | |
| Console.Write(" {0}", cellChar); | |
| } | |
| Console.WriteLine(); | |
| } | |
| } | |
| string PlayerName() { | |
| if (player == Player.White) { | |
| return "白"; | |
| } | |
| if (player == Player.Black) { | |
| return "黒"; | |
| } | |
| return "謎の人物"; | |
| } | |
| void AskCommand() { | |
| Console.WriteLine("{0}の手番です。石を落とす位置を入力してね", PlayerName()); | |
| while (true) { | |
| try { | |
| int pos = int.Parse(Console.ReadLine()); | |
| Cell toFall = Cell.Empty; | |
| if (player == Player.Black) { | |
| toFall = Cell.Black; | |
| } else if (player == Player.White) { | |
| toFall = Cell.White; | |
| } | |
| board.FallCell(toFall, pos); | |
| break; | |
| } catch (OverflowException _e) { | |
| Console.WriteLine("空いている列を選んでね"); | |
| } catch (Exception _ignore) { | |
| Console.WriteLine("上の範囲内の数字で入力し直してね"); | |
| } | |
| } | |
| } | |
| void NextTurn() { | |
| if (player == Player.Black) { | |
| player = Player.White; | |
| } else if (player == Player.White) { | |
| player = Player.Black; | |
| } | |
| } | |
| public void Run() { | |
| StartGame(); | |
| while (playing) { | |
| Flush(); | |
| AskCommand(); | |
| NextTurn(); | |
| } | |
| } | |
| } | |
| class Hamlet { | |
| static int Main() { | |
| var view = new GameView(8, 8); | |
| view.Run(); | |
| return 0; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment