using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace TestPiktiv { public class TicTacToe { private const int WinLength = 3; private int height = 15; private int width = 15; private char[,] buf; private bool isRunning; private char[,] board; private char[,] matchState; private (List p1, List p2) score = (new List(), new List()); private ConsoleKey[] xKeys = [ ConsoleKey.D1, ConsoleKey.D2, ConsoleKey.D3, ConsoleKey.D4, ConsoleKey.D5, ConsoleKey.D6, ConsoleKey.D7, ConsoleKey.D8, ConsoleKey.D9 ]; private int[,] magicSquare = new int[3, 3] { {4, 9, 2}, {3, 5, 7}, {8, 1, 6}, }; private bool player; private string winText; private bool shouldEndGame; public static void Main(string[] args) { TicTacToe game = new(); game.Start(); } public void Start() { Console.Clear(); buf = new char[height, width]; Console.CursorVisible = false; InitializeBoard(); BuildBoard(matchState); isRunning = true; shouldEndGame = false; player = true; Tick(); Console.CursorVisible = true; } private void InitializeBoard() { matchState = new char[3, 3]; var index = 1; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { char inc = index++.ToString()[0]; matchState[i, j] = inc; } } BuildBoard(matchState); } private void ClearBuffer() { for (int i = 0; i < height; i++) { for (int j = 0; j < height; j++) { buf[i, j] = ' '; } } } private void BuildBoard(char[,] state) { board = new char[7, 7] { {'╔','═','╦','═','╦','═','╗'}, {'║',state[0,0],'║',state[0,1],'║',state[0,2],'║'}, {'╠','═','╬','═','╬','═','╣'}, {'║',state[1,0],'║',state[1,1],'║',state[1,2],'║'}, {'╠','═','╬','═','╬','═','╣'}, {'║',state[2,0],'║',state[2,1],'║',state[2,2],'║'}, {'╚','═','╩','═','╩','═','╝'}, }; } private void SolveBoard(int index) { int h = index / 3; int w = index % 3; if (matchState[h, w] != 'X' && matchState[h, w] != 'O') { matchState[h, w] = player ? 'X' : 'O'; CheckWiningCondition_CheckNeighbors((h, w)); player = !player; } } // a fun/hacky way of solving this problem using a magic square where the sum of a // speific set of numbers on all lines and columns is always 15 private void CheckWiningCondition_MagicSquare((int h, int w) index) { if (player) { score.p1.Add(magicSquare[index.h, index.w]); if (CheckMagicSquare(score.p1)) { HandleWin(player); return; } return; } score.p2.Add(magicSquare[index.h, index.w]); if (CheckMagicSquare(score.p2)) { HandleWin(!player); } } private void CheckWiningCondition_CheckNeighbors((int h, int w) index) { if (CheckNeighbors(index)) { HandleWin(player); } } // solved by scanning the direct neighbors, optimized solution that works with any board size private bool CheckNeighbors((int h, int w) index) { char symbol = matchState[index.h, index.w]; var rows = matchState.GetLength(0); var columns = matchState.GetLength(1); (int dh, int dw)[] axes = [(0, 1), (1, 0), (-1, 1), (1, 1)]; foreach (var (dh, dw) in axes) { int found = 1; for (int k = 1; ; k++) { int r = index.h + dh * k; int l = index.w + dw * k; if (r < 0 || r >= rows || l < 0 || l >= columns) { break; } if (matchState[r, l] != symbol) { break; } found++; } for (int k = 1; ; k++) { int r = index.h - dh * k; int l = index.w - dw * k; if (r < 0 || r >= rows || l < 0 || l >= columns) { break; } if (matchState[r, l] != symbol) { break; } found++; } if (found >= WinLength) { return true; } } return false; } private bool CheckMagicSquare(List claimed) { if (claimed.Count < 3) { return false; } for (int i = 0; i < claimed.Count; i++) { for (int j = i + 1; j < claimed.Count; j++) { for (int k = j + 1; k < claimed.Count; k++) { if (claimed[i] + claimed[j] + claimed[k] == 15) { return true; } } } } return false; } private void HandleWin(bool player) { winText = player ? "Player 1 won!" : "Player 2 won!"; shouldEndGame = true; } private void Tick() { while (isRunning) { CheckStatus(HandleInput()); Render(); Thread.Sleep(40); if (shouldEndGame) { EndGame(); } } } private void CheckStatus(ConsoleKey consoleKey) { if (xKeys.Contains(consoleKey)) { SolveBoard(Array.IndexOf(xKeys, consoleKey)); } for (int i = 0; i < matchState.GetLength(0); i++) { for (int j = 0; j < matchState.GetLength(1); j++) { if (matchState[i, j] != 'X' && matchState[i, j] != 'O') { return; } } } shouldEndGame = true; } private ConsoleKey HandleInput() { if (Console.KeyAvailable) { var key = Console.ReadKey(true).Key; if (key == ConsoleKey.Escape) { shouldEndGame = true; } return key; } return ConsoleKey.None; } private void Render() { ClearBuffer(); BuildBoard(matchState); AddBoardToBuffer(); Present(); } private void AddBoardToBuffer() { for (int i = 4; i < height - 4; i++) { for (int j = 4; j < width - 4; j++) { buf[i, j] = board[i - 4, j - 4]; } } } private void Present() { var sb = new StringBuilder(height * (width + 1)); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { sb.Append(buf[i, j]); } if (i < height + 1) { sb.Append('\n'); } } Console.SetCursorPosition(0, 0); Console.Write(sb.ToString()); } private void EndGame() { Console.SetCursorPosition(0, height + 1); Console.WriteLine(winText); Console.WriteLine("Game Over. Press any key to restart!"); Console.WriteLine("..or press Esc to exit!"); var key = Console.ReadKey(true).Key; if (key == ConsoleKey.Escape) { isRunning = false; return; } Start(); } } }