From 1a110b213a2bc9867b1185176b8a483ecf8edfb0 Mon Sep 17 00:00:00 2001 From: Shuozhe Date: Sun, 25 Jul 2021 01:21:22 +0200 Subject: [PATCH] added Brain for second Player --- Controllers/TicTacTocController.cs | 4 +- Data/DataAccessContext.TicTacToc.cs | 2 +- Data/ITicTacToctRepository.cs | 2 +- Logic/AI/TicTacTocBrain.cs | 70 +++++++++++++++++++---------- Logic/TicTacTocBoard.cs | 2 + Logic/TicTacTocManager.cs | 62 ++++++++++++++++++++----- Logic/TicTacTocTypes.cs | 8 ++-- Startup.cs | 2 +- 8 files changed, 109 insertions(+), 43 deletions(-) diff --git a/Controllers/TicTacTocController.cs b/Controllers/TicTacTocController.cs index 4969a2a..6609607 100644 --- a/Controllers/TicTacTocController.cs +++ b/Controllers/TicTacTocController.cs @@ -35,10 +35,10 @@ namespace ProjectGrid.Controllers [HttpGet] [Route("api/ttt/Restart")] - public void RestartGame() + public void RestartGame(TicTacToc.Mode mode = TicTacToc.Mode.CROSS_PLAYER) { _logger.LogTrace("RestartGame called."); - _manager.Restart(); + _manager.Restart(mode); } [HttpPost] diff --git a/Data/DataAccessContext.TicTacToc.cs b/Data/DataAccessContext.TicTacToc.cs index d6bae2d..91554e5 100644 --- a/Data/DataAccessContext.TicTacToc.cs +++ b/Data/DataAccessContext.TicTacToc.cs @@ -16,7 +16,7 @@ namespace ProjectGrid.Data } - public Models.TicTacTocBrain GetBoard() + public TicTacTocBoard GetBoard() { throw new System.NotImplementedException(); } diff --git a/Data/ITicTacToctRepository.cs b/Data/ITicTacToctRepository.cs index 46fcdfc..86a584a 100644 --- a/Data/ITicTacToctRepository.cs +++ b/Data/ITicTacToctRepository.cs @@ -4,7 +4,7 @@ namespace ProjectGrid.Data { public interface ITicTacToctRepository { - TicTacTocBrain GetBoard(); + TicTacTocBoard GetBoard(); bool AddPiece(TicTacTocRequest user); } diff --git a/Logic/AI/TicTacTocBrain.cs b/Logic/AI/TicTacTocBrain.cs index 172488c..e7e3726 100644 --- a/Logic/AI/TicTacTocBrain.cs +++ b/Logic/AI/TicTacTocBrain.cs @@ -2,6 +2,7 @@ using ProjectGrid.Models; using ProjectGrid.TicTacToc; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace ProjectGrid.AI @@ -13,10 +14,10 @@ namespace ProjectGrid.AI private List _prio; - public TicTacTocBrain(TicTacTocBoard board, Player player) + public TicTacTocBrain(TicTacTocBoard board, int player) { _board = board; - _player = (int)player; + _player = player; var rand = new Random(); _prio = (new List { 0, 1, 2, 3, 4, 5, 6, 7, 8 }).OrderBy(i => rand.Next()).ToList(); @@ -24,45 +25,68 @@ namespace ProjectGrid.AI public int Turn() { - if (_board.Field[5] == (int)Player.EMPTY) + if (_board.Field[4] == (int)Player.EMPTY) { //_board.SetFieldValue(_player, 5); - return 5; + return 4; } + int blocking = -1; for (int x = 0; x < TicTacTocBoard._winning.GetLength(0); x++) { - var _1 = TicTacTocBoard._winning[x, 0]; - var _2 = TicTacTocBoard._winning[x, 1]; - var _3 = TicTacTocBoard._winning[x, 2]; + var _1 = _board.Field[TicTacTocBoard._winning[x, 0]]; + var _2 = _board.Field[TicTacTocBoard._winning[x, 1]]; + var _3 = _board.Field[TicTacTocBoard._winning[x, 2]]; - int result = Match2(_1, _2, _3); - if (result > 0) - { - //_board.SetFieldValue(_player, ); - return TicTacTocBoard._winning[x, result]; - } + // any field set? + if (_1 + _2 + _3 == 0) + continue; + + var result = MatchWinning(_1, _2, _3); + if (result.Item1 >= 0) + return TicTacTocBoard._winning[x, result.Item1]; + + if (result.Item2 >= 0) + blocking = TicTacTocBoard._winning[x, result.Item2]; } + if (blocking >= 0) + return blocking; + foreach (var i in _prio) if (_board.Field[i] == 0) return i; + Debug.Fail("Brain found no valid move!"); return -1; } - private int Match2(int _1, int _2, int _3) + private (int win, int block) MatchWinning(int _1, int _2, int _3) { - if (_1 == _player && _2 == _player && _3 == 0) - return 3; + (int win, int block) result = (-1, -1); + if (_1 == _2 && _1 != 0 && _3 == 0) + { + if (_1 == _player) + result.win = 2; + else + result.block = 2; + } + else if (_1 == _3 && _1 != 0 && _2 == 0) + { + if (_1 == _player) + result.win = 1; + else + result.block = 1; + } + else if (_2 == _3 && _2 != 0 && _1 == 0) + { + if (_1 == _player) + result.win = 0; + else + result.block = 0; + } - if (_1 == _player && _2 == 0 && _3 == _player) - return 2; - - if (_1 == 0 && _2 == _player && _3 == _player) - return 1; - - return 0; + return result; } } } \ No newline at end of file diff --git a/Logic/TicTacTocBoard.cs b/Logic/TicTacTocBoard.cs index 7323b8a..c546edf 100644 --- a/Logic/TicTacTocBoard.cs +++ b/Logic/TicTacTocBoard.cs @@ -15,6 +15,8 @@ namespace ProjectGrid.Models private int _lastSetValue; public int LastSetValue => _lastSetValue; + public bool Full => !!!_field.Contains(0); + private int PosToIdx(int x, int y) { Debug.Assert(x >= 0 && x <= 3 diff --git a/Logic/TicTacTocManager.cs b/Logic/TicTacTocManager.cs index c537244..8986e1f 100644 --- a/Logic/TicTacTocManager.cs +++ b/Logic/TicTacTocManager.cs @@ -1,3 +1,4 @@ +using ProjectGrid.AI; using ProjectGrid.Models; using ProjectGrid.TicTacToc; using System; @@ -22,6 +23,9 @@ namespace ProjectGrid } private State _state; + private Mode _mode; + + private TicTacTocBrain _brain; //private ITicTacToctRepository _repo; @@ -36,7 +40,7 @@ namespace ProjectGrid { //_repo = repo; _currentPlayer = 1; - Restart(Mode.TWO_PLAYER); + Restart(Mode.CROSS_PLAYER); } public TicTacTocResponse NextMove(TicTacTocRequest move) @@ -45,19 +49,50 @@ namespace ProjectGrid if (_state != State.GAME_OVER) { - _board.SetFieldValue(_currentPlayer, move.Field); - - response.Winning = _board.ValueWon(_currentPlayer); - if (_currentPlayer > 0 && response.Winning != null) + if (_board.SetFieldValue(_currentPlayer, move.Field)) { - response.PlayerWon = _currentPlayer; - _state = State.GAME_OVER; - _playerScore[_currentPlayer]++; + response.Winning = _board.ValueWon(_currentPlayer); + if (_currentPlayer > 0 && response.Winning != null) + { + response.PlayerWon = _currentPlayer; + _state = State.GAME_OVER; + _playerScore[_currentPlayer]++; + } + else if (null != _brain) + { + if (_board.Full) + { + _playerScore[0]++; + _state = State.GAME_OVER; + response.PlayerWon = 0; + return response; + } + int ai = 3 - _currentPlayer; + var turn = _brain.Turn(); + Console.WriteLine($"Brain made move {turn} for player {ai}"); + _board.SetFieldValue(ai, turn); + response.Winning = _board.ValueWon(ai); + if (response.Winning != null) + { + response.PlayerWon = ai; + _playerScore[ai]++; + _state = State.GAME_OVER; + } + } + else + { + // switch between 1 and 2 + _currentPlayer = 3 - _currentPlayer; + } } - // switch between 1 and 2 - _currentPlayer = 3 - _currentPlayer; + if (_board.Full) + { + _playerScore[0]++; + _state = State.GAME_OVER; + } } + response.Board = _board.Field; response.PlayerScore = new List(_playerScore); @@ -67,9 +102,14 @@ namespace ProjectGrid public void Restart(Mode mode) { - _currentPlayer = new Random().Next(1, 2); + _mode = mode; + _currentPlayer = 1; _board = new TicTacTocBoard(); _state = State.PLAYING; + + _brain = null; + if (Mode.TWO_PLAYER != mode) + _brain = new TicTacTocBrain(_board, 3 - (int)mode); } diff --git a/Logic/TicTacTocTypes.cs b/Logic/TicTacTocTypes.cs index 97fa6b9..b601378 100644 --- a/Logic/TicTacTocTypes.cs +++ b/Logic/TicTacTocTypes.cs @@ -4,14 +4,14 @@ namespace ProjectGrid.TicTacToc { public enum Mode { - TWO_PLAYER, - CROSS_PLAYER, - CIRCLE_PLAYER, + TWO_PLAYER = 0, + CROSS_PLAYER = 1, + CIRCLE_PLAYER = 2, } public enum Player { - EMPTY = 0; + EMPTY = 0, CROSS = 1, CIRCLE = 2, } diff --git a/Startup.cs b/Startup.cs index ecd124d..9199bb5 100644 --- a/Startup.cs +++ b/Startup.cs @@ -47,7 +47,7 @@ namespace ProjectGrid // Manager services.AddSingleton(new TicTacTocManager()); - services.AddScoped(); + services.AddScoped(); // DataAccess services.AddScoped();