added script

This commit is contained in:
Sebastian Bularca
2026-06-25 14:15:53 +02:00
commit 2a851ee121
2 changed files with 334 additions and 0 deletions

332
TicTacToe.cs Normal file
View File

@@ -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<int> p1, List<int> p2) score = (new List<int>(), new List<int>());
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<int> 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();
}
}
}