From 2a851ee121be4684ed1e0c3f0386bfcc0ce6d693 Mon Sep 17 00:00:00 2001 From: Sebastian Bularca Date: Thu, 25 Jun 2026 14:15:53 +0200 Subject: [PATCH] added script --- .gitignore | 2 + TicTacToe.cs | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 .gitignore create mode 100644 TicTacToe.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1746e32 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/TicTacToe.cs b/TicTacToe.cs new file mode 100644 index 0000000..ace69ee --- /dev/null +++ b/TicTacToe.cs @@ -0,0 +1,332 @@ +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(); + } + + } +}