Skip to content

Instantly share code, notes, and snippets.

@peterthorsteinson
Last active September 26, 2024 18:15
Show Gist options
  • Save peterthorsteinson/6e282d9cfb58bb500899d6ca4ed9f17d to your computer and use it in GitHub Desktop.
Save peterthorsteinson/6e282d9cfb58bb500899d6ca4ed9f17d to your computer and use it in GitHub Desktop.
// https://en.wikipedia.org/wiki/Tic-tac-toe and http://www.se16.info/hgb/tictactoe.htm
// If played properly, tic-tac-toe will end in a draw, making tic-tac-toe a futile game.
// Total number of X and O placements on a 3x3 grid = 9! = 362880, but many games end before completely filling in the grid.
// Also, many tic-tac-toe outcomes are logically equivalent in terms of reflection and rotation symmetries.
using System;
namespace TicTacToe
{
class Program
{
static void Main()
{
while (true) // each iteration of this outer loop is a new game
{
int i = 1; // center cell position in 0, 1, 2 columns
int j = 1; // center cell position in 0, 1, 2 rows
bool xIsNext = true; // true if next move is X, false if next move is O
string outcome = ""; // gets the string "X" if X wins, "O" if O wins, or "" for a draw
string[,] board = { { "", "", "" }, { "", "", "" }, { "", "", "" } };
int moveCount = 0; // can never be more than 9 moves where each X or O placement is a single move
Console.Clear();
WriteAt(0, 0, " | | ");
WriteAt(0, 1, "---+---+---");
WriteAt(0, 2, " | | ");
WriteAt(0, 3, "---+---+---");
WriteAt(0, 4, " | | ");
Console.SetCursorPosition(10, 10);
Console.WriteLine("Use arrow keys to move from cell to cell and hit enter to place a token.");
Console.CursorLeft = 1;
Console.CursorTop = 0;
Console.SetCursorPosition(1 + (4 * i), 2 * j); // offsets and multiliers to position in cell centers
while (true) // each iteration of this inner loop is the placement of an "X" or a "Y as a single move
{
ConsoleKeyInfo consoleKeyInfo = Console.ReadKey(true); // get a key stroke without echo to output
switch (consoleKeyInfo.Key)
{
case ConsoleKey.RightArrow:
i = (i + 1) % 3;
Console.SetCursorPosition(1 + (4 * i), 2 * j);
break;
case ConsoleKey.LeftArrow:
i = (i + 2) % 3;
Console.SetCursorPosition(1 + (4 * i), 2 * j);
break;
case ConsoleKey.DownArrow:
j = (j + 1) % 3;
Console.SetCursorPosition(1 + (4 * i), 2 * j);
break;
case ConsoleKey.UpArrow:
j = (j + 2) % 3;
Console.SetCursorPosition(1 + (4 * i), 2 * j);
break;
case ConsoleKey.Enter:
if (board[i, j] != "") { Console.Beep(); break; } // cannot place X or O in cell that is already played
string token = xIsNext ? "X" : "O"; // select token to place in cell for this move
board[i, j] = token;
WriteAt(1 + (4 * i), 2 * j, token);
xIsNext = !xIsNext; // alternate X/O token for next move
moveCount++; // count number of completed moves so far
break;
}
outcome = WhoWon(board, moveCount);
if (outcome != "") break;
}
Console.SetCursorPosition(10, 10);
Console.WriteLine("Game Over. Winner: " + outcome + ". Hit q to quit, or hit any other key to play again.");
if (Console.ReadKey(true).Key == ConsoleKey.Q) break; else continue;
}
Console.Clear();
}
protected static void WriteAt(int x, int y, string s)
{
try
{
Console.SetCursorPosition(x, y);
Console.Write(s);
}
catch (ArgumentOutOfRangeException e)
{
Console.Clear();
Console.WriteLine(e.Message);
}
}
protected static string WhoWon(string[,] board, int moveCount)
{
// check to see if X won yet
if (board[0, 0] == "X" && board[0, 1] == "X" && board[0, 2] == "X" ||
(board[1, 0] == "X" && board[1, 1] == "X" && board[1, 2] == "X") ||
(board[2, 0] == "X" && board[2, 1] == "X" && board[2, 2] == "X") ||
(board[0, 0] == "X" && board[1, 0] == "X" && board[2, 0] == "X") ||
(board[0, 1] == "X" && board[1, 1] == "X" && board[2, 1] == "X") ||
(board[0, 2] == "X" && board[1, 2] == "X" && board[2, 2] == "X") ||
(board[0, 0] == "X" && board[1, 1] == "X" && board[2, 2] == "X") ||
(board[0, 2] == "X" && board[1, 1] == "X" && board[2, 0] == "X")) return "X";
// check to see if O won yet
if (board[0, 0] == "O" && board[0, 1] == "O" && board[0, 2] == "O" ||
(board[1, 0] == "O" && board[1, 1] == "O" && board[1, 2] == "O") ||
(board[2, 0] == "O" && board[2, 1] == "O" && board[2, 2] == "O") ||
(board[0, 0] == "O" && board[1, 0] == "O" && board[2, 0] == "O") ||
(board[0, 1] == "O" && board[1, 1] == "O" && board[2, 1] == "O") ||
(board[0, 2] == "O" && board[1, 2] == "O" && board[2, 2] == "O") ||
(board[0, 0] == "O" && board[1, 1] == "O" && board[2, 2] == "O") ||
(board[0, 2] == "O" && board[1, 1] == "O" && board[2, 0] == "O")) return "O";
// max number of completed moves is 9
if (moveCount == 9) return "Draw";
return ""; // game still in play (no winner yet, and ;ess than 9 moves have been completed
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment