/// @copydoc IThinker.Think /// <seealso cref="IThinker.Think"/> public FutureMove Think(Board board, CancellationToken ct) { // Check how many pieces current player has int roundPieces = board.PieceCount(board.Turn, PShape.Round); int squarePieces = board.PieceCount(board.Turn, PShape.Square); // Chose a random piece int pieceRand = random.Next(roundPieces + squarePieces); PShape shape = pieceRand < roundPieces ? PShape.Round : PShape.Square; // Chose a random free position int col; do { // Get a random position col = random.Next(board.cols); // Is this task to be cancelled? if (ct.IsCancellationRequested) { return(FutureMove.NoMove); } }while (board.IsColumnFull(col)); // Return the random move return(new FutureMove(col, shape)); }
public PSolver(PShape shape1, PShape shape2, PContact[] contacts, int num) { s1 = shape1; s2 = shape2; b1 = s1._parent; b2 = s2._parent; fric = (float) System.Math.Sqrt(s1._fric * s2._fric); rest = (float) System.Math.Sqrt(s1._rest * s2._rest); cs = contacts; numContacts = num; for (int i = 0; i < numContacts; i++) { PContact c = cs[i]; c.rel1 = c.pos.Sub(b1.pos); c.rel2 = c.pos.Sub(b2.pos); c.massN = PTransformer.CalcEffectiveMass(b1, b2, c.rel1, c.rel2, c.normal); c.massT = PTransformer.CalcEffectiveMass(b1, b2, c.rel1, c.rel2, c.tangent); c.relVel = PTransformer .CalcRelativeVelocity(b1, b2, c.rel1, c.rel2); float rvn = c.relVel.Dot(c.normal); if (rvn < -0.5F) c.targetVelocity = System.Math.Max(rest * -rvn,0.0F); else c.targetVelocity = 0.0F; c.tangent.Set(c.normal.y, -c.normal.x); c.localRel1.Set(c.rel1.x, c.rel1.y); c.localRel2.Set(c.rel2.x, c.rel2.y); b1.mAng.Transpose().MulEqual(c.localRel1); b2.mAng.Transpose().MulEqual(c.localRel2); } }
private void NewPlay( Pos pos, Piece piece, Player player, PColor color, PShape shape) { if (piece.color == color || piece.shape == shape) { if (player == Player.me) { myWinCorridors.Add( new Play( pos.col, pos.row, oneGood)); } else { myWinCorridors.Add( new Play( pos.col, pos.row, blockedCorridor)); } } }
protected override void setup() { size(512 * displayAspectW, 512, P2D); square = createShape(RECT, 0, 0, width / 2 - 25 * 2, height - 25 * 2); square.disableStyle(); circle = createShape(ELLIPSE, 0, 0, 100, 100); circle.setFill(color(0, 255, 0)); star = createShape(); star.beginShape(TRIANGLE_FAN); star.vertex(0, 0); star.vertex(0, -50); star.vertex(14, -20); star.vertex(47, -15); star.vertex(23, 7); star.vertex(29, 40); star.vertex(0, 25); star.vertex(-29, 40); star.vertex(-23, 7); star.vertex(-47, -15); star.vertex(-14, -20); star.vertex(0, -50); star.endShape(); recycle(); }
// Make shape selection visible in the UI private void SelectShape(PColor player, PShape shape) { // Keep the currently selected shape selectedShapes[(int)player] = shape; // Update UI widgets which depend on the shape selection ShapeSelected.Invoke(player, shape); }
/// @copydoc ColorShapeLinks.Common.AI.AbstractThinker.Setup /// <seealso cref="ColorShapeLinks.Common.AI.AbstractThinker.Setup"/> public override void Setup(string str) { selectedCol = Cols / 2; selectedShape = PShape.Round; Console.Clear(); Console.WriteLine("\n\n\n"); }
private void triangleToolStripMenuItem_Click(object sender, EventArgs e) { pointShape = PShape.T; circleToolStripMenuItem.Checked = false; squareToolStripMenuItem.Checked = false; triangleToolStripMenuItem.Checked = true; }
public PSortableObject(PShape s, PSortableAABB a, float v, bool b) { this.parent = s; this.aabb = a; this.value_ren = v; this.begin = b; }
public PSortableObject(PShape s, PSortableAABB a, float v, bool b) { this.parent = s; this.aabb = a; this.value_ren = v; this.begin = b; }
/// <summary> /// Updates a hashed key with one extra position; /// </summary> /// <param name="column"> column position of the move</param> /// <param name="row"> row position of the move</param> /// <param name="color"> color of the piece</param> /// <param name="shape"> shape of the piece</param> /// <param name="oldKey"> old key to add the move to</param> /// <returns> /// New key with the desired move incorporated in. /// </returns> public ulong UpdateHash(int column, int row, PColor color, PShape shape, ulong oldKey) { ulong newKey = 0; // Xor occupied locations in turn newKey = oldKey ^ zobristKey[column * row][(int)shape][(int)color]; return(newKey); }
private void CheckWinCorridors( Player player, PColor color, PShape shape, Board board) { Piece piece; foreach (IEnumerable <Pos> enumerable in board.winCorridors) { foreach (Pos pos in enumerable) { positions.Add(pos); } } foreach (Pos pos in positions) { if (board[pos.row, pos.col] == null) { if (pos.col == 0) { if (board[pos.row, pos.col + 1] != null) { piece = (Piece)board[pos.row, pos.col + 1]; NewPlay(pos, piece, player, color, shape); } } else if (pos.col == board.cols - 1) { if (board[pos.row, pos.col - 1] != null) { piece = (Piece)board[pos.row, pos.col - 1]; NewPlay(pos, piece, player, color, shape); } } else { if (board[pos.row, pos.col + 1] != null) { piece = (Piece)board[pos.row, pos.col + 1]; NewPlay(pos, piece, player, color, shape); } if (board[pos.row, pos.col - 1] != null) { piece = (Piece)board[pos.row, pos.col - 1]; NewPlay(pos, piece, player, color, shape); } } } } }
/// @copydoc ColorShapeLinks.Common.AI.AbstractThinker.Setup /// <seealso cref="ColorShapeLinks.Common.AI.AbstractThinker.Setup"/> public override void Setup(string str) { selectedCol = Cols / 2; selectedShape = PShape.Round; if (str != null && str.Trim().Length > 0) { Console.WriteLine( $"Human player ignored these parameters: \"{str}\""); } }
/// <summary> /// Get greyscale level. /// </summary> /// <param name="x">X.</param> /// <param name="y">Y.</param> /// <returns>Value.</returns> protected byte GetGreyscaleLevel(int x, int y) { // Is this position is stored in cache ? if (!Cache.IsCached(x, y)) { Cache.Update(x, y, PShape.GetValue(x, y).Red); } return(Cache.Level); }
public virtual int Collide(PShape s1, PShape s2, PContact[] cs) { PCollider collider = null; bool flip = false; switch (s1._type) { case Physics.PShapeType.BOX_SHAPE: case Physics.PShapeType.CONVEX_SHAPE: switch (s2._type) { case Physics.PShapeType.BOX_SHAPE: case Physics.PShapeType.CONVEX_SHAPE: collider = new PPolygonPolygonCollider(); break; case Physics.PShapeType.CIRCLE_SHAPE: collider = new PCirclePolygonCollider(); flip = true; break; case Physics.PShapeType.CONCAVE_SHAPE: default: break; } break; case Physics.PShapeType.CIRCLE_SHAPE: switch (s2._type) { case Physics.PShapeType.BOX_SHAPE: case Physics.PShapeType.CONVEX_SHAPE: collider = new PCirclePolygonCollider(); break; case Physics.PShapeType.CIRCLE_SHAPE: collider = new PCircleCirlceCollider(); break; case Physics.PShapeType.CONCAVE_SHAPE: default: break; } break; case Physics.PShapeType.CONCAVE_SHAPE: default: break; } if (collider == null) { return 0; } if (flip) { int res = collider.Collide(s2, s1, cs); if (res > 0) { for (int i = 0; i < res; i++) { cs[i].normal.NegateLocal(); } } return res; } return collider.Collide(s1, s2, cs); }
public override FutureMove Think(Board board, CancellationToken ct) { Winner winner; PColor colorOfOurAI = board.Turn; possibleMoves.Clear(); nonLosingMoves.Clear(); for (int col = 0; col < Cols; col++) { if (board.IsColumnFull(col)) { continue; } for (int shp = 0; shp < 2; shp++) { PShape shape = (PShape)shp; if (board.PieceCount(colorOfOurAI, shape) == 0) { continue; } possibleMoves.Add(new FutureMove(col, shape)); board.DoMove(shape, col); winner = board.CheckWinner(); // immediately board.UndoMove(); if (winner.ToPColor() == colorOfOurAI) { return(new FutureMove(col, shape)); } else if (winner.ToPColor() != colorOfOurAI.Other()) { nonLosingMoves.Add(new FutureMove(col, shape)); } } } if (nonLosingMoves.Count > 0) { return(nonLosingMoves[random.Next(nonLosingMoves.Count)]); } return(possibleMoves[random.Next(possibleMoves.Count)]); }
void changeShape(PShape newShape) { shape = newShape; if (newShape == PShape.circle) { sprite.sprite = gm.circleSprite; } else if (newShape == PShape.hexagon) { sprite.sprite = gm.hexagonSprite; } else if (newShape == PShape.diamond) { sprite.sprite = gm.diamondSprite; } }
/// <summary> /// Check enemy colums for a winning move according to shapes. /// </summary> /// <param name="board"></param> /// <param name="col"></param> /// <returns>A move that blocks enemy win.</returns> private FutureMove?CheckEnemyColsShape(Board board, int col) { FutureMove? move; List <bool> threeInLine = new List <bool>(); Piece piece; PShape enemyShape = color == PColor.White ? PShape.Square : PShape.Round; for (int i = 0; i < board.rows; i++) { if (board[i, col] == null) { return(null); } piece = (Piece)board[i, col]; if (piece.shape == enemyShape) { threeInLine.Add(true); } else { threeInLine.RemoveRange(0, threeInLine.Count); } if (threeInLine.Count == 3) { if (board[i + 1, col].HasValue || i == board.rows) { piece = (Piece)board[i + 1, col]; if (piece.shape == shape) { threeInLine.RemoveRange(0, threeInLine.Count); } } else { if (!board.IsColumnFull(col)) { return(move = new FutureMove(col, shape)); } } } } return(move = null); }
public PSolver(PShape shape1, PShape shape2, PContact[] contacts, int num) { s1 = shape1; s2 = shape2; b1 = s1._parent; b2 = s2._parent; fric = (float)System.Math.Sqrt(s1._fric * s2._fric); rest = (float)System.Math.Sqrt(s1._rest * s2._rest); cs = contacts; numContacts = num; for (int i = 0; i < numContacts; i++) { PContact c = cs[i]; c.rel1 = c.pos.Sub(b1.pos); c.rel2 = c.pos.Sub(b2.pos); c.massN = PTransformer.CalcEffectiveMass(b1, b2, c.rel1, c.rel2, c.normal); c.massT = PTransformer.CalcEffectiveMass(b1, b2, c.rel1, c.rel2, c.tangent); c.relVel = PTransformer .CalcRelativeVelocity(b1, b2, c.rel1, c.rel2); float rvn = c.relVel.Dot(c.normal); if (rvn < -0.5F) { c.targetVelocity = System.Math.Max(rest * -rvn, 0.0F); } else { c.targetVelocity = 0.0F; } c.tangent.Set(c.normal.y, -c.normal.x); c.localRel1.Set(c.rel1.x, c.rel1.y); c.localRel2.Set(c.rel2.x, c.rel2.y); b1.mAng.Transpose().MulEqual(c.localRel1); b2.mAng.Transpose().MulEqual(c.localRel2); } }
public virtual int Collide(PShape s1, PShape s2, PContact[] cs) { PCollider collider = null; bool flip = false; switch (s1._type) { case Physics.PShapeType.BOX_SHAPE: case Physics.PShapeType.CONVEX_SHAPE: switch (s2._type) { case Physics.PShapeType.BOX_SHAPE: case Physics.PShapeType.CONVEX_SHAPE: collider = new PPolygonPolygonCollider(); break; case Physics.PShapeType.CIRCLE_SHAPE: collider = new PCirclePolygonCollider(); flip = true; break; case Physics.PShapeType.CONCAVE_SHAPE: default: break; } break; case Physics.PShapeType.CIRCLE_SHAPE: switch (s2._type) { case Physics.PShapeType.BOX_SHAPE: case Physics.PShapeType.CONVEX_SHAPE: collider = new PCirclePolygonCollider(); break; case Physics.PShapeType.CIRCLE_SHAPE: collider = new PCircleCirlceCollider(); break; case Physics.PShapeType.CONCAVE_SHAPE: default: break; } break; case Physics.PShapeType.CONCAVE_SHAPE: default: break; } if (collider == null) { return(0); } if (flip) { int res = collider.Collide(s2, s1, cs); if (res > 0) { for (int i = 0; i < res; i++) { cs[i].normal.NegateLocal(); } } return(res); } return(collider.Collide(s1, s2, cs)); }
/// <summary> /// Get number of remaining pieces with the given color and shape. /// </summary> /// <param name="color">Pieces color.</param> /// <param name="shape">Pieces shape.</param> /// <returns> /// Number of remaining pieces with the given color and shape. /// </returns> public int PieceCount(PColor color, PShape shape) => numberOfPieces[new Piece(color, shape)];
/// <summary> /// Make a move, return row /// </summary> /// <param name="shape"> /// Shape of piece used in move (color is obtained from /// <see cref="Board.Turn"/>). /// </param> /// <param name="col">Column where to drop piece.</param> /// <returns>Row where piece was placed or -1 if move is invalid.</returns> public int DoMove(PShape shape, int col) { // The row were to place the piece, initially assumed to be the top row int row = rows - 1; // The color of the piece to place, depends on who's playing PColor color = Turn; // The piece to place Piece piece = new Piece(color, shape); // If the column is not a valid column, there is a client code bug, // so let's throw an exception if (col < 0 || col >= cols) { throw new InvalidOperationException($"Invalid board column: {col}"); } // If we already found a winner, there is a client code bug, so let's // throw an exception if (CheckWinner() != Winner.None) { throw new InvalidOperationException( "Game is over, unable to make further moves."); } // If there are no more pieces of the specified kind, there is a client // bug, so let's throw an exception if (numberOfPieces[piece] == 0) { throw new InvalidOperationException( $"No more {piece} pieces available"); } // If column is already full, return negative value, indicating the // move is invalid if (board[col, row].HasValue) { return(-1); } // // If we get here, move is valid, so let's do it // // Find row where to place the piece for (int r = row - 1; r >= 0 && !board[col, r].HasValue; r--) { row = r; } // Place the piece board[col, row] = piece; // Decrease the piece count numberOfPieces[piece]--; // Remember the move moveSequence.Push(new Pos(row, col)); // Increment number of moves numMoves++; // Update turn Turn = Turn == PColor.White ? PColor.Red : PColor.White; // Return true, indicating the move was successful return(row); }
/// <summary> /// Method used to play a move. /// </summary> /// <param name="board"></param> /// <param name="ct"></param> /// <returns>Move</returns> public FutureMove Think(Board board, CancellationToken ct) { FutureMove?test; Play play; random = new System.Random(); color = board.Turn; if (color == PColor.White) { shape = PShape.Round; } else { shape = PShape.Square; } if (shape == PShape.Round && board.PieceCount(color, PShape.Round) == 0) { shape = PShape.Square; } else if (shape == PShape.Square && board.PieceCount(color, PShape.Square) == 0) { shape = PShape.Round; } Check(board); test = PlayPiece(board); if (test != null) { return((FutureMove)test); } test = CheckEnemy(board); if (test != null) { return((FutureMove)test); } play = Negamax(board, board.Turn, maxDepth, ct); if (test != null) { if (play.pos == null) { return(FutureMove.NoMove); } else { return(new FutureMove((int)play.pos, PShape.Round)); } } if (allPiece.Count % 2 == 0) { test = CheckWinCorridors(board); } else { test = CheckEnemyWinCorridors(board); } if (test != null) { return((FutureMove)test); } /*int roundPieces = board.PieceCount(board.Turn, PShape.Round); * int squarePieces = board.PieceCount(board.Turn, PShape.Square); * shape = squarePieces < roundPieces ? PShape.Round : PShape.Square;*/ return(new FutureMove(random.Next(0, board.cols), shape)); }
public FutureMove Think(Board board, CancellationToken ct) { FutureMove?test; Play play; random = new Random(); color = board.Turn; if (color == PColor.White) { shape = PShape.Round; } else { shape = PShape.Square; } if (shape == PShape.Round && board.PieceCount(color, PShape.Round) == 0) { shape = PShape.Square; } else if (shape == PShape.Square && board.PieceCount(color, PShape.Square) == 0) { shape = PShape.Round; } Check(board); test = CheckPlayer(board, Player.me); if (test != null) { return((FutureMove)test); } test = CheckPlayer(board, Player.enemy); if (test != null) { return((FutureMove)test); } PColor myColor = board.Turn; PShape myShape = shape; CheckWinCorridors(Player.me, myColor, myShape, board); PColor enemyColor = color == PColor.White ? PColor.Red : PColor.White; PShape enemyShape = shape == PShape.Round ? PShape.Square : PShape.Round; CheckWinCorridors(Player.enemy, enemyColor, enemyShape, board); play = Negamax(board, board.Turn, maxDepth, ct); if (test != null) { if (play.posCol == null) { return(FutureMove.NoMove); } else { return(new FutureMove((int)play.posCol, PShape.Round)); } } return(new FutureMove(random.Next(0, board.cols), shape)); }
/// @copydoc ColorShapeLinks.Common.AI.IThinker.Think /// <remarks> /// This method asks the human to play. /// </remarks> /// <seealso cref="ColorShapeLinks.Common.AI.IThinker.Think"/> public override FutureMove Think(Board board, CancellationToken ct) { // By default, no move is performed in case of timeout FutureMove move = FutureMove.NoMove; // No thinking notification has taken place DateTime lastNotificationTime = DateTime.MinValue; // Set thinking notification interval between frames to 20ms TimeSpan notificationInterval = TimeSpan.FromMilliseconds(20); // Calculate the time limit for the human to play DateTime timeLimit = DateTime.Now + TimeSpan.FromMilliseconds(TimeLimitMillis); // Show some info on what keys are used for input OnThinkingInfo("T to toggle piece, < > to change selected column"); // If a certain type of piece is not available, make the other // type the selected one if (board.PieceCount(board.Turn, PShape.Round) == 0) { selectedShape = PShape.Square; } if (board.PieceCount(board.Turn, PShape.Square) == 0) { selectedShape = PShape.Round; } // If the current column is full, iterate column selection until // one is not while (board.IsColumnFull(selectedCol)) { selectedCol++; if (selectedCol >= Cols) { selectedCol = 0; } } // Wait for human input at most until the cancellation token is // activated while (true) { // Was a key pressed? if (Console.KeyAvailable) { // Retrieve key ConsoleKey key = Console.ReadKey().Key; // Check if it was a column increment key if (key == ConsoleKey.RightArrow || key == ConsoleKey.D || key == ConsoleKey.NumPad6 || key == ConsoleKey.D6) { // Increment column... do { // ...making sure a full column is not selectable, // and wraping around selectedCol++; if (selectedCol >= Cols) { selectedCol = 0; } } while (board.IsColumnFull(selectedCol)); } // Check if it was a column decrement key else if (key == ConsoleKey.LeftArrow || key == ConsoleKey.A || key == ConsoleKey.NumPad4 || key == ConsoleKey.D4) { // Decrement column... do { // ...making sure a full column is not selectable, // and wraping around selectedCol--; if (selectedCol < 0) { selectedCol = Cols - 1; } } while (board.IsColumnFull(selectedCol)); } // Check if it was a piece toggle key else if (key == ConsoleKey.T) { // Toggle piece if the other type of piece is available if (selectedShape == PShape.Round && board.PieceCount(board.Turn, PShape.Square) > 0) { selectedShape = PShape.Square; } else if (selectedShape == PShape.Square && board.PieceCount(board.Turn, PShape.Round) > 0) { selectedShape = PShape.Round; } } // Check if it was a piece drop key else if (key == ConsoleKey.Enter) { // Drop piece and get out of the input loop move = new FutureMove(selectedCol, selectedShape); break; } } // If cancellation token is activated, terminate input loop if (ct.IsCancellationRequested) { break; } // Is it time for another thinking notification? if (DateTime.Now > lastNotificationTime + notificationInterval) { // Show dialog Console.CursorLeft = 0; Console.Write(String.Format( "Col [{0,4}] | Shape [{1,7}] | Time [{2,14}]", selectedCol, selectedShape, timeLimit - DateTime.Now)); Console.CursorLeft = 0; // Update last notification time lastNotificationTime = DateTime.Now; } } // Return chosen move return(move); }
/// <summary> /// Method used to check enemy WinCorridors and play according to it, /// in order to block is line /// </summary> /// <param name="board"></param> /// <returns>A play for the AI to use.</returns> private FutureMove?CheckEnemyWinCorridors(Board board) { Piece piece; PColor enemyColor = color == PColor.White ? PColor.Red : PColor.White; PShape enemyShape = shape == PShape.Round ? PShape.Square : PShape.Round; foreach (IEnumerable <Pos> enumerable in board.winCorridors) { foreach (Pos pos in enumerable) { positions.Add(pos); } } foreach (Pos pos in positions) { if (board[pos.row, pos.col] == null) { if (pos.col == 0) { if (board[pos.row, pos.col + 1] != null) { piece = (Piece)board[pos.row, pos.col + 1]; if (piece.color == enemyColor || piece.shape == enemyShape) { return(new FutureMove(pos.col, shape)); } } } else if (pos.col == board.cols - 1) { if (board[pos.row, pos.col - 1] != null) { piece = (Piece)board[pos.row, pos.col - 1]; if (piece.color == enemyColor || piece.shape == enemyShape) { return(new FutureMove(pos.col, shape)); } } } else { if (board[pos.row, pos.col + 1] != null) { piece = (Piece)board[pos.row, pos.col + 1]; if (piece.color == enemyColor || piece.shape == enemyShape) { return(new FutureMove(pos.col, shape)); } } if (board[pos.row, pos.col - 1] != null) { piece = (Piece)board[pos.row, pos.col - 1]; if (piece.color == enemyColor || piece.shape == enemyShape) { return(new FutureMove(pos.col, shape)); } } } } } return(null); }
/// <summary> /// Negamax. /// </summary> /// <param name="board"></param> /// <param name="turn"></param> /// <param name="maxDepth"></param> /// <param name="ct"></param> /// <returns>A play for the AI to use.</returns> private Play Negamax(Board board, PColor turn, int maxDepth, CancellationToken ct) { Play bestMove = new Play(null, int.MinValue); PColor proxTurn = turn == PColor.Red ? PColor.White : PColor.Red; bool stup = false; if (ct.IsCancellationRequested) { return(new Play(null, 0)); } else { if (maxDepth <= 0) { return(bestMove); } foreach (Play play in myWinCorridors) { for (int j = 0; j < board.cols; j++) { int pos = j; if (board[(int)play.pos, j] == null) { int roundPieces = board.PieceCount(board.Turn, PShape.Round); int squarePieces = board.PieceCount(board.Turn, PShape.Square); if (shape == PShape.Round) { if (roundPieces == 0) { shape = PShape.Square; } } else { if (squarePieces == 0) { shape = PShape.Round; } } Play move = default; board.DoMove(shape, j); maxDepth--; if (board.CheckWinner() != Winner.None) { stup = true; } if (!stup) { move = Negamax(board, proxTurn, maxDepth, ct); } board.UndoMove(); move.score = -move.score; if (move.score > bestMove.score) { bestMove.score = move.score; bestMove.pos = pos; } } } } /*for (int i = 0; i < board.rows; i++) * { * for (int j = 0; j < board.cols; j++) * { * int pos = j; * if (board[i, j] == null) * { * int roundPieces = board.PieceCount(board.Turn, PShape.Round); * int squarePieces = board.PieceCount(board.Turn, PShape.Square); * if (shape == PShape.Round) * { * if (roundPieces == 0) * { * shape = PShape.Square; * } * } * else * { * if (squarePieces == 0) * { * shape = PShape.Round; * } * } * Play move = default; * board.DoMove(shape, j); * maxDepth--; * if (board.CheckWinner() != Winner.None) * { * stup = true; * } * if (!stup) * { * move = Negamax(board, proxTurn, maxDepth, ct); * } * board.UndoMove(); * move.score = -move.score; * if (move.score > bestMove.score) * { * bestMove.score = move.score; * bestMove.pos = pos; * } * } * } * }*/ return(bestMove); } }
private Play Negamax(int depth, Board board, PColor turn, CancellationToken cancellationToken, int alpha = int.MinValue, int beta = int.MaxValue) { Play selectedMove = new Play(null, int.MinValue); PColor nextTurn = (turn == PColor.Red) ? PColor.White : PColor.Red; if (cancellationToken.IsCancellationRequested) { return(new Play(null, 0)); } if (depth <= 0) { int tempScore = _random.Next(0, 100); if (board.CheckWinner() != Winner.None) { tempScore = 300; } tempScore += board.winCorridors.Count() * 10; if (tempScore % 2 != 0) { tempScore *= -1; } selectedMove.Score = tempScore; return(selectedMove); } for (int j = 0; j < board.cols; j++) { int column = j; for (int i = 0; i < board.rows; i++) { if (board[i, j] == null) { int roundPieces = board.PieceCount(board.Turn, PShape.Round); int squarePieces = board.PieceCount(board.Turn, PShape.Square); Play move = default; if (roundPieces > 0) { shape = PShape.Round; board.DoMove(shape, j); if ((board.CheckWinner() == Winner.None)) { move = Negamax(depth - 1, board, nextTurn, cancellationToken, -beta, -alpha); } board.UndoMove(); move.Score = -move.Score; if (move.Score > bestMove.Score) { bestMove = move; } if (bestMove.Score > alpha) { alpha = bestMove.Score; } if (bestMove.Score >= beta) { bestMove.Score = alpha; bestMove.Position = column; return(bestMove); } } if (squarePieces > 0) { shape = PShape.Square; board.DoMove(shape, j); if ((board.CheckWinner() == Winner.None)) { move = Negamax(depth - 1, board, nextTurn, cancellationToken, -beta, -alpha); } board.UndoMove(); if (move.Score > bestMove.Score) { bestMove = move; } if (bestMove.Score > alpha) { alpha = bestMove.Score; } if (bestMove.Score >= beta) { bestMove.Score = alpha; bestMove.Position = column; return(bestMove); } } } } } return(bestMove); }
/// <summary>Create a new piece.</summary> /// <param name="color">The piece color.</param> /// <param name="shape">The piece shape.</param> public Piece(PColor color, PShape shape) { this.color = color; this.shape = shape; }
/// <summary>Is the piece of the specified color and shape?</summary> /// <param name="color">The piece color.</param> /// <param name="shape">The piece shape.</param> /// <returns> /// `true` if the piece has the specified color and shape, `false` /// otherwise. /// </returns> public bool Is(PColor color, PShape shape) => this.color == color && this.shape == shape;
private Play Negamax( Board board, PColor turn, int maxDepth, CancellationToken ct) { Play bestMove = new Play(null, null, int.MinValue); PColor proxTurn = turn == PColor.Red ? PColor.White : PColor.Red; if (ct.IsCancellationRequested) { return(new Play(null, null, 0)); } else { if (cDepth == maxDepth) { return(bestMove); } cDepth++; foreach (Play play in myWinCorridors) { for (int j = 0; j < board.cols; j++) { int pos = j; if (board[(int)play.posCol, (int)play.posRow] == null) { int roundPieces = board.PieceCount(board.Turn, PShape.Round); int squarePieces = board.PieceCount(board.Turn, PShape.Square); if (shape == PShape.Round) { if (roundPieces == 0) { shape = PShape.Square; } else if (squarePieces == 0) { shape = PShape.Round; } } Play move = default; board.DoMove(shape, j); if (board.CheckWinner() == Winner.None) { move = Negamax(board, proxTurn, maxDepth, ct); } board.UndoMove(); move.score = -move.score; if (move.score > bestMove.score) { bestMove.score = move.score; bestMove.posCol = pos; } } } } return(bestMove); } }
public override void Alter(LTimerContext timer) { for (int i = 0; i < keySize; i++) { ActionKey act = (ActionKey)keyActions.Get(i); if (act.IsPressed()) { act.Act(elapsedTime); if (act.isReturn) { return; } } } if (content.IsVisible()) { ProcessEvents(); content.UpdateNode(timer.GetMilliseconds()); } if (usePhysics) { if (_dt < 0) { _manager.Step(timer.GetMilliseconds()); } else { _manager.Step(_dt); } } if (follow != null) { if (usePhysics) { _manager.Offset(follow.GetX(), follow.GetY()); } foreach (TileMap tile in tiles) { float offsetX = GetHalfWidth() - follow.GetX(); offsetX = MathUtils.Min(offsetX, 0); offsetX = MathUtils.Max(offsetX, GetWidth() - tile.GetWidth()); float offsetY = GetHalfHeight() - follow.GetY(); offsetY = MathUtils.Min(offsetY, 0); offsetY = MathUtils .Max(offsetY, GetHeight() - tile.GetHeight()); SetOffset(tile, offsetX, offsetY); tile.Update(elapsedTime); } } foreach (SpriteBatchObject o in objects) { if (usePhysics) { PBody body = (PBody)CollectionUtils.Get(_Bodys, o); if (body != null) { PShape shape = body.Inner_shapes()[0]; float rotation = (shape.GetAngle() * MathUtils.RAD_TO_DEG) % 360; AABB aabb = shape.GetAABB(); o.SetLocation(_manager.GetScreenX(aabb.minX), _manager.GetScreenY(aabb.minY)); o.SetRotation(rotation); } } o.Update(elapsedTime); if (updateListener != null) { updateListener.Act(o, elapsedTime); } } Update(elapsedTime); Commits(); }
// Awake is called when the script instance is being loaded private void Awake() { // Top and bottom pole padding const float polePadding = 0.1f; // Ground instance GameObject groundInst, messageBox; // Top-left of ground sprite renderer Vector3 gTopLeft; // Bounds of different sprite renderers Bounds gBounds, plBounds, aBounds, pcBounds; // Get reference to the camera Camera camera = GameObject.Find("Main Camera").GetComponent <Camera>(); // Get reference to the match view configuration IMatchViewConfig viewConfig = GetComponentInParent <IMatchViewConfig>(); // Get value of last move animation length in seconds lastMoveAnimLength = viewConfig.LastMoveAnimLength; // /////////////////////////////////// // // Get references to essential prefabs // // /////////////////////////////////// // whiteRoundPiece = Resources.Load <GameObject>("Prefabs/PieceWhiteRound"); whiteSquarePiece = Resources.Load <GameObject>("Prefabs/PieceWhiteSquare"); redRoundPiece = Resources.Load <GameObject>("Prefabs/PieceRedRound"); redSquarePiece = Resources.Load <GameObject>("Prefabs/PieceRedSquare"); pole = Resources.Load <GameObject>("Prefabs/Pole"); ground = Resources.Load <GameObject>("Prefabs/Ground"); arrowButton = Resources.Load <GameObject>("Prefabs/ArrowButton"); playerPanel = Resources.Load <GameObject>("Prefabs/PlayerPanel"); // //////////////////////////////////////////// // // Initialize required variables and coroutines // // //////////////////////////////////////////// // // Instantiate a string builder, used for keeping messages messages = new StringBuilder(); // We just started, so game is not finished yet finished = false; // Get reference to the match configuration matchConfig = GetComponentInParent <IMatchConfig>(); // Get reference to the session data and the game board matchData = GetComponentInParent <IMatchDataProvider>(); board = matchData.Board; // Both players have the round shapes initially selected by default selectedShapes = new PShape[] { PShape.Round, PShape.Round }; // Instantiate Unity events for shape selection and board updating ShapeSelected = new ColorShapeEvent(); BoardUpdated = new UnityEvent(); // Create matrix for placing game objects representing pieces pieces = new GameObject[board.rows, board.cols]; // Create array for UI arrow script objects uiArrows = new HumanMoveButton[board.cols]; // Instantiate the message queue messageQueue = new Queue <string>(); // Get reference to the "Message Box" canvas game object, set the // reference to the main camera, and get a reference to the UI text // to display the messages messageBox = GameObject.Find("MessageBox").gameObject; messageBox.GetComponent <Canvas>().worldCamera = camera; messageBoxText = messageBox.GetComponentInChildren <Text>(); // Initialize message box coroutine StartCoroutine(UpdateMessageBox()); // /////////////////////////////////////// // // Initialize and place game board objects // // /////////////////////////////////////// // // Instantiate ground groundInst = Instantiate(ground, transform); // Determine where ground starts, since everything will be placed // with respect to the ground gBounds = groundInst.GetComponent <SpriteRenderer>().bounds; gTopLeft = new Vector3(gBounds.min.x, gBounds.max.y, 0); // Get pole bounds plBounds = pole.GetComponent <SpriteRenderer>().bounds; // Get arrow bounds aBounds = arrowButton.GetComponent <SpriteRenderer>().bounds; // Get piece bounds (any will do) pcBounds = redRoundPiece.GetComponent <SpriteRenderer>().bounds; // Instantiate poles and arrows for (int c = 0; c < board.cols; c++) { GameObject currPole, currArrow; // Instantiate current pole currPole = Instantiate( pole, new Vector3( gTopLeft.x + (c + 1) * (gBounds.size.x / (board.cols + 1)), gTopLeft.y + plBounds.extents.y - polePadding, 1), Quaternion.identity, transform); currPole.name = $"Pole{c}"; // Instantiate current arrow currArrow = Instantiate( arrowButton, new Vector3( gTopLeft.x + (c + 1) * (gBounds.size.x / (board.cols + 1)), gTopLeft.y + plBounds.size.y + polePadding + aBounds.extents.y, 4), Quaternion.identity, transform); currArrow.name = $"Arrow{c}"; // Keep reference to the UI arrow script uiArrows[c] = currArrow.GetComponent <HumanMoveButton>(); // Set the arrow's column uiArrows[c].Column = c; // Listen to arrow clicks in order to perform move selection uiArrows[c].Click.AddListener(OnMoveSelected); // Enable or disable arrow depending on who's playing currArrow.SetActive(matchData.CurrentThinker is HumanThinker); } // These will be necessary for calculating the positions of the // pieces leftPoleBase = new Vector2( gTopLeft.x + gBounds.size.x / (board.cols + 1), gTopLeft.y); distBtwPoles = gBounds.size.x / (board.cols + 1); totalHeightForPieces = plBounds.size.y - 2 * polePadding; // The scale of the pieces will the minimum between... piecesScale = Mathf.Min( // ...half of distance between the poles divided by the original // width of the prefabs... (distBtwPoles / 2) / pcBounds.size.x, // ...and the available space for each piece in a pole, divided // by the original height of the prefabs (totalHeightForPieces / board.rows) / pcBounds.size.y); // Keep the length of the pieces (equal in x and y directions) piecesLength = piecesScale * pcBounds.size.y; // /////////////////// // // Setup player panels // // /////////////////// // // Instantiate player panels GameObject[] playerPanels = { Instantiate(playerPanel, transform), Instantiate(playerPanel, transform) }; // Initialize an array of piece sprites, which will simplify // passing the correct sprite for each shape in each player panel Sprite[,] pieceSprites = { { whiteRoundPiece.GetComponent <SpriteRenderer>().sprite, whiteSquarePiece.GetComponent <SpriteRenderer>().sprite, }, { redRoundPiece.GetComponent <SpriteRenderer>().sprite, redSquarePiece.GetComponent <SpriteRenderer>().sprite, } }; // Initialize panels for each player for (int i = 0; i < 2; i++) { // Current player PColor player = (PColor)i; // Get current panel GameObject panel = playerPanels[i]; // Get current panel's rect transform RectTransform rtPanel = panel.GetComponent <RectTransform>(); // Get current panel toggles for selecting shape Toggle[] toggles = panel.GetComponentsInChildren <Toggle>(); // Setup event camera in panel panel.GetComponent <Canvas>().worldCamera = camera; // Position panel rtPanel.position = new Vector3( i == 0 // First panel to the right ? gBounds.center.x - gBounds.extents.x / 2 // Second panel to the left : gBounds.center.x + gBounds.extents.x / 2, gBounds.min.y - rtPanel.rect.height / 2 * rtPanel.localScale.y, rtPanel.position.z ); // Set player name in panel panel.GetComponentInChildren <Text>().text = matchData.GetThinker(player).ToString(); // Configure toggles for selecting shape for (int j = 0; j < 2; j++) { // Current shape PShape shape = (PShape)j; // Setup correct sprite for the toggle toggles[j] .transform .GetChild(1) .GetComponent <Image>() .sprite = pieceSprites[i, j]; // Delegate to be called at start and after each move, // which: // 1. Updates piece count in current player's toggle UI // widget // 2. Enables/disables toggle interaction depending on // who's playing UnityAction setToggles = () => { // 1. // Count for current player and shape int count = board.PieceCount(player, shape); // Update label to update with shape count toggles[(int)shape].GetComponentInChildren <Text>() .text = count.ToString(); // If count reached zero, swap shape selection if (count == 0) { SelectShape( player, shape == PShape.Round ? PShape.Square : PShape.Round); toggles[(int)shape].interactable = false; } // 2. else { // Player is human, is its turn and game not over, // enable toggle if (matchData.GetThinker(player) is HumanThinker && player == board.Turn && !finished) { toggles[(int)shape].interactable = true; } // Otherwise disable toggle else { toggles[(int)shape].interactable = false; } } }; // Invoke delegate to initialize toggles setToggles.Invoke(); // Make this delegate be called after each move BoardUpdated.AddListener(setToggles); // Wire up method for listening to piece swap events toggles[j].onValueChanged.AddListener( b => { if (b) { SelectShape(player, shape); } }); } // Wire up listener for programatically changing selected shape // in current player's toggle ShapeSelected.AddListener((PColor p, PShape s) => { if (p == player) { toggles[(int)s].isOn = true; } }); } }
private FutureMove?CheckCols( Board board, int col, Player player, CheckType checkType) { List <bool> threeInLine = new List <bool>(3); Piece piece; PColor enemyColor = color == PColor.White ? PColor.Red : PColor.White; PShape enemyShape = color == PColor.White ? PShape.Square : PShape.Round; for (int i = 0; i < board.rows; i++) { if (board[i, col] == null) { return(null); } piece = (Piece)board[i, col]; if (checkType == CheckType.color) { if (piece.color == board.Turn) { threeInLine.Add(true); } else { threeInLine.RemoveRange(0, threeInLine.Count); } } else { if (piece.shape == shape) { threeInLine.Add(true); } else { threeInLine.RemoveRange(0, threeInLine.Count); } } if (threeInLine.Count == 3) { if (!board.IsColumnFull(col)) { if (board[i + 1, col].HasValue || i == board.rows) { piece = (Piece)board[i + 1, col]; if (checkType == CheckType.color) { if (piece.color == enemyColor) { threeInLine.RemoveRange( 0, threeInLine.Count); } else if (piece.shape == enemyShape) { threeInLine.RemoveRange( 0, threeInLine.Count); } } } else { if (player == Player.me) { myWinCorridors.Add(new Play(col, i, win)); } else { myWinCorridors.Add(new Play(col, i, block)); } return(new FutureMove(col, shape)); } } } } return(null); }