diff --git a/gui/GUI/Board.cs b/gui/GUI/Board.cs index 5570441..ffa110b 100644 --- a/gui/GUI/Board.cs +++ b/gui/GUI/Board.cs @@ -7,7 +7,10 @@ public enum PieceColour { White, Black }; public enum PieceType { Pawn, Knight, Bishop, Rook, Queen, King }; // The colour before the game result indicates which colour has lost. public enum GameStatus { Inactive, Active, Stalemate, WhiteCheckmate, - BlackCheckmate, WhiteAdjudicate, BlackAdjudicate, WhiteTime, BlackTime }; + BlackCheckmate, WhiteAdjudicate, BlackAdjudicate, WhiteTime, BlackTime, + DrawRepetition, DrawFifty, DrawInsuffientMaterial }; + // If a move was a castle, capture, etc. + public enum MoveResult { None, PawnMove, Capture, KingsideCastle, QueensideCastle } /** * @brief Representation of the board as a whole. @@ -22,10 +25,16 @@ public class Board public virtual bool BlackCastledL { get; set; } public virtual bool WhiteCastledL { get; set; } public virtual PieceColour PlayerToMove { get; set; } + public virtual byte EnPassantSquare { get; protected set; } + public virtual PieceColour EnPassantColour { get; protected set; } public readonly byte[] castleDestinations = { 2, 6, 56, 62 }; public readonly byte[] pawnPromotionDestinations = { 0, 1, 2, 3, 4, 5, 6, 7, 56, 57, 58, 59, 60, 61, 62, 63 }; + public readonly byte[] enPassantStartSquares = { 8, 9, 10, 11, 12, 13, 14, 15, + 48, 49, 50, 51, 52, 53, 54, 55 }; + public readonly byte[] enPassantEndSquares = { 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39 }; /** * @brief Default constructor. @@ -42,6 +51,8 @@ public Board (bool empty = false) BlackCastledL = true; WhiteCastledL = true; PlayerToMove = PieceColour.White; + EnPassantSquare = 0; + EnPassantColour = PieceColour.White; if (empty) { Squares = new Square[64]; @@ -94,13 +105,13 @@ public Board (bool empty = false) * * Copies the list of pieces from one board to another. */ - public Board(Board other) + public Board(Board other, bool copyLists = false) { Squares = new Square[64]; for (int i = 0; i < 64; i++) { Squares [i] = new Square (); if (other.Squares [i].Piece != null) { - Squares [i].Piece = new Piece (other.Squares [i].Piece); + Squares [i].Piece = new Piece (other.Squares [i].Piece, copyLists); } } BlackCheck = other.BlackCheck; @@ -110,6 +121,8 @@ public Board(Board other) BlackCastledR = other.BlackCastledR; WhiteCastledR = other.WhiteCastledR; PlayerToMove = other.PlayerToMove; + EnPassantSquare = other.EnPassantSquare; + EnPassantColour = other.EnPassantColour; } public void AddPiece(PieceColour colour, PieceType type, int position) @@ -154,8 +167,10 @@ public bool IsMoveValid(byte source, byte destination) * Makes a move, switches the @c PlayerToMove variable, * and updates piece legal moves. */ - public void MakeMove(byte source, byte destination, PieceType? promoteTo = null) + public MoveResult MakeMove(byte source, byte destination, PieceType? promoteTo = null) { + MoveResult ret = MoveResult.None; + if (!IsMoveValid (source, destination)) { throw new InvalidOperationException ("Invalid move entered."); } @@ -188,6 +203,7 @@ public void MakeMove(byte source, byte destination, PieceType? promoteTo = null) if (movingPiece.Type == PieceType.King && (source == 4 || source == 60) && Array.IndexOf (castleDestinations, destination) != -1) { + ret = destination - source > 0 ? MoveResult.KingsideCastle : MoveResult.QueensideCastle; Square castleRookSquare = destination - source > 0 ? Squares [destination + 1] : Squares [destination - 2]; Squares [destination].Piece = movingPiece; @@ -197,6 +213,18 @@ Squares [destination - source > 0 ? destination + 1].Piece = castleRookSquare.Piece; castleRookSquare.Piece = null; } else { + // Handle pawn moves + if (Squares [source].Piece.Type == PieceType.Pawn) { + ret = MoveResult.PawnMove; + // Handle en passant creation + if (Array.IndexOf (enPassantStartSquares, source) > -1 && Array.IndexOf (enPassantEndSquares, destination) > -1) { + EnPassantColour = Squares [source].Piece.Colour; + EnPassantSquare = EnPassantColour == PieceColour.White ? (byte)(destination + 8) : (byte)(destination - 8); + } + } + if (Squares [destination].Piece != null) { + ret = MoveResult.Capture; + } switch (promoteTo) { case PieceType.Bishop: Squares [destination].Piece = new Piece (movingPiece.Colour, PieceType.Bishop); @@ -215,8 +243,21 @@ Squares [destination - source > 0 ? Squares [source].Piece = null; break; default: - Squares [destination].Piece = movingPiece; - Squares [source].Piece = null; + // Handle en passant capture + if (movingPiece.Type == PieceType.Pawn && destination == EnPassantSquare && EnPassantSquare != 0) { + byte captureSquare = EnPassantColour == PieceColour.White ? (byte)(destination - 8) : (byte)(destination + 8); + Squares [destination].Piece = movingPiece; + Squares [captureSquare].Piece = null; + Squares [source].Piece = null; + ret = MoveResult.Capture; + EnPassantSquare = 0; + } else { + Squares [destination].Piece = movingPiece; + Squares [source].Piece = null; + if (movingPiece.Type != PieceType.Pawn) { + EnPassantSquare = 0; + } + } break; } @@ -232,6 +273,7 @@ Squares [destination - source > 0 ? } PiecePseudoLegalMoves.GeneratePseudoLegalMoves (this); PieceLegalMoves.GenerateLegalMoves (this); + return ret; } /** @@ -484,10 +526,24 @@ public Piece(PieceColour colour, PieceType type) * * @param other the piece from which to copy. */ - public Piece(Piece other) + public Piece(Piece other, bool copyLists = false) { Colour = other.Colour; Type = other.Type; + if (copyLists == true) + { + if (other.PseudoLegalMoves != null) + { + PseudoLegalMoves = new List(other.PseudoLegalMoves); + } + if (other.LegalMoves != null) + { + LegalMoves = new List(other.LegalMoves); + } + } + TimesAttacked = other.TimesAttacked; + TimesDefended = other.TimesDefended; + HasMoved = other.HasMoved; } /** diff --git a/gui/GUI/DummyBoard.cs b/gui/GUI/DummyBoard.cs index 7a5ceb8..487012a 100644 --- a/gui/GUI/DummyBoard.cs +++ b/gui/GUI/DummyBoard.cs @@ -24,6 +24,8 @@ public class DummyBoard : Board public override bool BlackCastledL { get; set; } public override bool WhiteCastledL { get; set; } public override PieceColour PlayerToMove { get; set; } + public override byte EnPassantSquare { get; protected set; } + public override PieceColour EnPassantColour { get; protected set; } public DummyBoard (Board other) { @@ -41,6 +43,8 @@ public DummyBoard (Board other) BlackCastledL = other.BlackCastledR; WhiteCastledL = other.WhiteCastledR; PlayerToMove = other.PlayerToMove; + EnPassantSquare = other.EnPassantSquare; + EnPassantColour = other.EnPassantColour; } // Returns value of captured piece, for use in DummyBoard.UndoMove @@ -63,6 +67,16 @@ Squares [destination - source > 0 ? destination + 1].Piece = castleRookSquare.Piece; castleRookSquare.Piece = null; } else { + // Handle en passant. + if (Squares [source].Piece.Type == PieceType.Pawn) { + if (Array.IndexOf (enPassantStartSquares, source) > -1 && Array.IndexOf (enPassantEndSquares, destination) > -1) { + EnPassantColour = Squares [source].Piece.Colour; + EnPassantSquare = EnPassantColour == PieceColour.White ? (byte)(destination + 8) : (byte)(destination - 8); + } else { + EnPassantSquare = 0; + } + } + capturedPiece = Squares [destination].Piece; switch (promoteTo) { case PieceType.Bishop: @@ -82,8 +96,17 @@ Squares [destination - source > 0 ? Squares [source].Piece = null; break; default: - Squares [destination].Piece = movingPiece; - Squares [source].Piece = null; + // Handle en passant capture + if (movingPiece.Type == PieceType.Pawn && destination == EnPassantSquare && EnPassantSquare != 0) { + byte captureSquare = EnPassantColour == PieceColour.White ? (byte)(destination - 8) : (byte)(destination + 8); + Squares [destination].Piece = movingPiece; + capturedPiece = Squares [captureSquare].Piece; + Squares [captureSquare].Piece = null; + Squares [source].Piece = null; + } else { + Squares [destination].Piece = movingPiece; + Squares [source].Piece = null; + } break; } } diff --git a/gui/GUI/GUI.csproj b/gui/GUI/GUI.csproj index 116a6b2..25c60de 100644 --- a/gui/GUI/GUI.csproj +++ b/gui/GUI/GUI.csproj @@ -33,27 +33,21 @@ False - gtk-sharp-2.0 False - gtk-sharp-2.0 False - glib-sharp-2.0 False - glade-sharp-2.0 False - gtk-sharp-2.0 False - gtk-sharp-2.0 @@ -89,6 +83,7 @@ + diff --git a/gui/GUI/GameHistory.cs b/gui/GUI/GameHistory.cs new file mode 100644 index 0000000..014a3e1 --- /dev/null +++ b/gui/GUI/GameHistory.cs @@ -0,0 +1,489 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace GUI +{ + public enum SpecifierType { None, File, Rank, Both } + + public class Move + { + public byte Source { get; set; } + public byte Destination { get; set; } + public PieceColour Colour { get; set; } + public Piece MovingPiece { get; set; } + public SpecifierType SpecifierRequired { get; set; } // Whether or not to specify the source square in the short algebraic notation + public PieceType? PromoteTo { get; set; } + public MoveResult Result { get; set; } + public string FEN { get; set; } + public int CheckOrCheckmate { get; set; } // 0 for none, 1 for check, 2 for checkmate + + public Move(byte source, byte destination, PieceColour colour, Piece movingPiece, + MoveResult result, string fen, int checkOrCheckmate, SpecifierType specifierRequired = SpecifierType.None, PieceType? promoteTo = null) + { + Source = source; + Destination = destination; + Colour = colour; + MovingPiece = movingPiece; + SpecifierRequired = specifierRequired; + PromoteTo = promoteTo; + Result = result; + FEN = fen; + CheckOrCheckmate = checkOrCheckmate; + } + + public Move(Move other) + { + Source = other.Source; + Destination = other.Destination; + Colour = other.Colour; + MovingPiece = other.MovingPiece; + SpecifierRequired = other.SpecifierRequired; + PromoteTo = other.PromoteTo; + Result = other.Result; + FEN = other.FEN; + CheckOrCheckmate = other.CheckOrCheckmate; + } + } + + public class GameHistory + { + private List history; + private List positions; + + // PGN Metadata + public string Event { get; private set; } + public string Site { get; private set; } + public string Date { get; private set; } + public string Round { get; private set; } + public string White { get; private set; } + public string Black { get; private set; } + public string Result { get; private set; } + + public int FiftyMoveRuleCount { get; private set; } + public int MoveCount { get; private set; } + + private static string[] columns = { "a", "b", "c", "d", "e", "f", "g", "h" }; + + public GameHistory () + { + history = new List (); + positions = new List (); + + Event = "Casual game."; + Site = System.Environment.MachineName; + Date = DateTime.Now.ToString("yyyy.MM.dd"); + Round = "1"; + White = "Unknown"; + Black = "Unknown"; + Result = "*"; + + FiftyMoveRuleCount = 0; + MoveCount = 1; + } + + public static string MoveToNotation(Move move) + { + if (move.Result == MoveResult.KingsideCastle) + { + return "O-O"; + } + else if (move.Result == MoveResult.QueensideCastle) + { + return "O-O-O"; + } + string movePiece = PieceToNotation (move.MovingPiece); + string moveSpecifier = ""; + if (move.SpecifierRequired != SpecifierType.None) { + moveSpecifier = GetSpecifier(move); + } + string captureSpecifier = ""; + if (move.Result == MoveResult.Capture) { + captureSpecifier = "x"; + } + string moveSquare = SquareToNotation (move.Destination); + string promoteTo = PromoteToNotation(move.PromoteTo); + string moveString = movePiece + moveSpecifier + captureSpecifier + moveSquare + promoteTo; + + if (move.CheckOrCheckmate == 1) { + moveString += "+"; + } else if (move.CheckOrCheckmate == 2) { + moveString += "#"; + } + return moveString; + } + + private static string SquareToNotation(byte square) + { + string column = columns [square % 8]; + string row = Convert.ToString (Math.Abs((int)(square / 8) - 7) + 1); + return column + row; + } + + private static string PieceToNotation(Piece piece) + { + string pieceNotation = ""; + switch (piece.Type) { + case PieceType.Pawn: + pieceNotation = ""; + break; + case PieceType.Knight: + pieceNotation = "N"; + break; + case PieceType.Bishop: + pieceNotation = "B"; + break; + case PieceType.Rook: + pieceNotation = "R"; + break; + case PieceType.King: + pieceNotation = "K"; + break; + case PieceType.Queen: + pieceNotation = "Q"; + break; + default: + break; + } + + return pieceNotation; + } + + private static string PromoteToNotation(PieceType? promoteTo) + { + switch (promoteTo) { + case null: + return null; + case PieceType.Knight: + return "=N"; + case PieceType.Bishop: + return "=B"; + case PieceType.Rook: + return "=R"; + case PieceType.Queen: + return "=Q"; + default: + return null; + } + } + + private static string GetSpecifier(Move move) + { + switch (move.SpecifierRequired) { + case SpecifierType.File: + return columns [move.Source % 8]; + case SpecifierType.Rank: + return Convert.ToString (Math.Abs((int)(move.Source / 8) - 7) + 1); + case SpecifierType.Both: + return SquareToNotation (move.Source); + default: + break; + } + return ""; + } + + public void AddMove(Move move, string fen) + { + history.Add (move); + positions.Add (fen); + if (move.Colour == PieceColour.Black) { + MoveCount++; + } + } + + public void UndoLastMove() + { + history.RemoveAt (history.Count - 1); + } + + public Move GetLastMove() + { + return new Move (history [history.Count - 1]); + } + + public GameStatus? UpdateFiftyMoveCount(MoveResult result) + { + if (result == MoveResult.PawnMove || result == MoveResult.Capture) { + FiftyMoveRuleCount = 0; + } else { + FiftyMoveRuleCount++; + } + if (FiftyMoveRuleCount >= 100) { + return GameStatus.DrawFifty; + } + return null; + } + + public GameStatus? CheckThreefoldRepetition() + { + Dictionary counts = positions.GroupBy (x => x) + .ToDictionary (g => g.Key, + g => g.Count ()); + if (counts.ContainsValue (3)) { + return GameStatus.DrawRepetition; + } + return null; + } + + public string ToPGNString() + { + string output = ""; + + // First, create the PGN headers + output += ("[Event \"" + Event + "\"]\n"); + output += ("[Site \"" + Site + "\"]\n"); + output += ("[Date \"" + Date + "\"]\n"); + output += ("[Round \"" + Round + "\"]\n"); + output += ("[White \"" + White + "\"]\n"); + output += ("[Black \"" + Black + "\"]\n"); + output += ("[Result \"" + Result + "\"]\n\n"); + + // Now we can print the moves + for (int i = 0; i < history.Count; i += 2) { + string moveOutput = ((i / 2) + 1) + ". "; + string whiteMove = ""; + string blackMove = ""; + + if (history [i].Result == MoveResult.KingsideCastle) { + moveOutput += "O-O"; + } else if (history [i].Result == MoveResult.QueensideCastle) { + moveOutput += "O-O-O"; + } else if (history [i].Colour == PieceColour.Black) { + whiteMove = "..."; + blackMove = MoveToNotation (history [i]); + } else if (history.Count % 2 != 0 && i == history.Count - 1) { // When the final move is a white move + whiteMove = MoveToNotation (history [i]); + blackMove = ""; + } else { + whiteMove = MoveToNotation (history [i]); + blackMove = MoveToNotation (history [i + 1]); + } + + moveOutput = moveOutput + whiteMove + " " + blackMove + " "; + output += moveOutput; + } + + return output; + } + + public static SpecifierType checkDisabiguationNeeded(Board theBoard, byte source, byte destination) + { + string pieceOnSquare = SquareToNotation(source); + if ((theBoard.Squares[source].Piece.Type == PieceType.Pawn) && (pieceOnSquare[0] != SquareToNotation(destination)[0])) + { + return SpecifierType.File; + } + bool needsFileSpecifier = false; + bool needsRankSpecifier = false; + for (int i = 0; i < theBoard.Squares.Length; i++) + { + if (theBoard.Squares[i].Piece != null) + { + if ((theBoard.Squares[i].Piece.Type == theBoard.Squares[source].Piece.Type) && ((byte)i != source)) + { + for (int j = 0; j < theBoard.Squares[i].Piece.LegalMoves.Count; j++) + { + if (theBoard.Squares[i].Piece.LegalMoves[j] == destination) + { + string otherPieceSquare = SquareToNotation((byte)i); + if (otherPieceSquare[0] != pieceOnSquare[0]) + { + needsFileSpecifier = true; + } + else + { + needsRankSpecifier = true; + } + } + } + } + } + } + if (needsFileSpecifier && needsRankSpecifier) + { + return SpecifierType.Both; + } + else if (needsFileSpecifier) + { + return SpecifierType.File; + } + else if (needsRankSpecifier) + { + return SpecifierType.Rank; + } + return SpecifierType.None; + } + + public static List> getPossibleMoveNotations(Board gameBoard) + { + List> possibleMoveNotations = new List>(); + + for (int i = 0; i < gameBoard.Squares.Length; i++) + { + if (gameBoard.Squares[i].Piece != null) + { + for (int j = 0; j < gameBoard.Squares[i].Piece.LegalMoves.Count; j++) + { + SpecifierType disambiguationNeeded = checkDisabiguationNeeded(gameBoard, (byte)i, gameBoard.Squares[i].Piece.LegalMoves[j]); + + Board copiedBoard = new Board(gameBoard, true); + MoveResult result = MoveResult.None; + if ((gameBoard.Squares[i].Piece.Type == PieceType.Pawn) && (Array.IndexOf(copiedBoard.pawnPromotionDestinations, gameBoard.Squares[i].Piece.LegalMoves[j]) != -1)) { + //Promotion + result = copiedBoard.MakeMove((byte)i, gameBoard.Squares[i].Piece.LegalMoves[j], PieceType.Queen); + + string copiedBoardFEN = copiedBoard.ToFEN (); + + int checkOrCheckmate = 0; + GameStatus mateState = copiedBoard.CheckForMate (); + if (mateState == GameStatus.WhiteCheckmate || mateState == GameStatus.BlackCheckmate) { + checkOrCheckmate = 2; + } else if (copiedBoard.WhiteCheck || copiedBoard.BlackCheck) { + checkOrCheckmate = 1; + } + + Move newMove = new Move((byte)i, gameBoard.Squares[i].Piece.LegalMoves[j], gameBoard.Squares[i].Piece.Colour, gameBoard.Squares[i].Piece, + result, copiedBoardFEN, checkOrCheckmate, disambiguationNeeded, PieceType.Queen); + Tuple newTuple = new Tuple(newMove, MoveToNotation(newMove)); + possibleMoveNotations.Add(newTuple); + newMove = new Move((byte)i, gameBoard.Squares[i].Piece.LegalMoves[j], gameBoard.Squares[i].Piece.Colour, gameBoard.Squares[i].Piece, + result, copiedBoardFEN, checkOrCheckmate, disambiguationNeeded, PieceType.Rook); + newTuple = new Tuple(newMove, MoveToNotation(newMove)); + possibleMoveNotations.Add(newTuple); + newMove = new Move((byte)i, gameBoard.Squares[i].Piece.LegalMoves[j], gameBoard.Squares[i].Piece.Colour, gameBoard.Squares[i].Piece, + result, copiedBoardFEN, checkOrCheckmate, disambiguationNeeded, PieceType.Bishop); + newTuple = new Tuple(newMove, MoveToNotation(newMove)); + possibleMoveNotations.Add(newTuple); + newMove = new Move((byte)i, gameBoard.Squares[i].Piece.LegalMoves[j], gameBoard.Squares[i].Piece.Colour, gameBoard.Squares[i].Piece, + result, copiedBoardFEN, checkOrCheckmate, disambiguationNeeded, PieceType.Knight); + newTuple = new Tuple(newMove, MoveToNotation(newMove)); + possibleMoveNotations.Add(newTuple); + } else { + result = copiedBoard.MakeMove((byte)i, gameBoard.Squares[i].Piece.LegalMoves[j]); + + string copiedBoardFEN = copiedBoard.ToFEN (); + + int checkOrCheckmate = 0; + GameStatus mateState = copiedBoard.CheckForMate (); + if (mateState == GameStatus.WhiteCheckmate || mateState == GameStatus.BlackCheckmate) { + checkOrCheckmate = 2; + } else if (copiedBoard.WhiteCheck || copiedBoard.BlackCheck) { + checkOrCheckmate = 1; + } + + Move newMove = new Move((byte)i, gameBoard.Squares[i].Piece.LegalMoves[j], gameBoard.Squares[i].Piece.Colour, gameBoard.Squares[i].Piece, + result, copiedBoardFEN, checkOrCheckmate, disambiguationNeeded, null); + Tuple newTuple = new Tuple(newMove, MoveToNotation(newMove)); + possibleMoveNotations.Add(newTuple); + } + } + } + } + return possibleMoveNotations; + } + + public static GameHistory importPGN(string PGN) + { + GameHistory newGameHistory = new GameHistory(); + //Parse tag pairs first + string startingFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; + while (PGN.Contains("[")) + { + string tagPair = PGN.Substring(PGN.IndexOf('[') + 1, PGN.IndexOf(']') - PGN.IndexOf('[') - 1); + PGN = PGN.Substring(PGN.IndexOf(']') + 1); + + switch (tagPair.Substring(0, tagPair.IndexOf(' '))) + { + case "Event": + newGameHistory.Event = tagPair.Substring(tagPair.IndexOf('"') + 1).TrimEnd('"'); + break; + case "Site": + newGameHistory.Site = tagPair.Substring(tagPair.IndexOf('"') + 1).TrimEnd('"'); + break; + case "Date": + newGameHistory.Date = tagPair.Substring(tagPair.IndexOf('"') + 1).TrimEnd('"'); + break; + case "Round": + newGameHistory.Round = tagPair.Substring(tagPair.IndexOf('"') + 1).TrimEnd('"'); + break; + case "White": + newGameHistory.White = tagPair.Substring(tagPair.IndexOf('"') + 1).TrimEnd('"'); + break; + case "Black": + newGameHistory.Black = tagPair.Substring(tagPair.IndexOf('"') + 1).TrimEnd('"'); + break; + case "Result": + newGameHistory.Result = tagPair.Substring(tagPair.IndexOf('"') + 1).TrimEnd('"'); + break; + case "FEN": + startingFEN = tagPair.Substring(tagPair.IndexOf('"') + 1).TrimEnd('"'); + break; + default: + break; + } + } + //Remove comments from PGN + while (PGN.Contains("{")) + { + PGN = PGN.Remove(PGN.IndexOf('{'), PGN.IndexOf('}') - PGN.IndexOf('{') + 1); + } + + //Take in moves one by one + FENParser importFEN = new FENParser(startingFEN); + Board gameBoard = importFEN.GetBoard(); + + PiecePseudoLegalMoves.GeneratePseudoLegalMoves(gameBoard); + PieceLegalMoves.GenerateLegalMoves(gameBoard); + + List> possibleMoveNotations = getPossibleMoveNotations(gameBoard); + string[] tokens = PGN.Split(null); + + for (int i = 0; i < tokens.Length; i++) + { + char[] trimChars = { '!', '?' }; + char[] checkChars = { '+', '#' }; + string moveNotation = tokens[i].Trim(trimChars); + + if (moveNotation.Contains('.')) + { + moveNotation = moveNotation.Substring(moveNotation.IndexOf('.') + 1); + } + + if (moveNotation.Length != 0) + { + //Console.WriteLine("***" + moveNotation + "***"); //debugging output + for (int j = 0; j < possibleMoveNotations.Count; j++) + { + //Console.Write(possibleMoveNotations[j].Item2 + " "); + if (moveNotation.Equals(possibleMoveNotations[j].Item2) || moveNotation.Equals(possibleMoveNotations[j].Item2.Trim(checkChars))) + { + gameBoard.MakeMove(possibleMoveNotations[j].Item1.Source, possibleMoveNotations[j].Item1.Destination, possibleMoveNotations[j].Item1.PromoteTo); + newGameHistory.AddMove(possibleMoveNotations[j].Item1, gameBoard.ToFEN().Split(' ')[0]); + possibleMoveNotations = getPossibleMoveNotations(gameBoard); + break; + } + } + //Console.WriteLine(""); //debugging output + } + } + return newGameHistory; + } + + public bool SavePGN(string filename) + { + StreamWriter PGNFile = new StreamWriter (filename); + + try { + PGNFile.WriteLine(ToPGNString()); + } catch(Exception ex) { + Console.Error.WriteLine ("(EE) Error writing to PGN file: " + ex.Message); + return false; + } + + PGNFile.Close (); + return true; + } + } +} + diff --git a/gui/GUI/MainWindow.cs b/gui/GUI/MainWindow.cs index b9becf7..0b2fdf4 100644 --- a/gui/GUI/MainWindow.cs +++ b/gui/GUI/MainWindow.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using System.Text.RegularExpressions; +using System.IO; using Gtk; using Cairo; @@ -19,6 +20,8 @@ public partial class MainWindow: Gtk.Window int materialDifference = 0; Cairo.Context boardContext; byte[] pieceValues = { 1, 3, 3, 5, 8 }; // Pawn, Knight, Bishop, Rook, Queen + byte[] whiteSquares = { 1, 3, 5, 7, 8, 10, 12, 14, 17, 19, 21, 23, 24, 26, 28, 30, 33, + 35, 37, 39, 40, 42, 44, 46, 49, 51, 53, 55, 56, 58, 60, 62 }; DateTime engineThinkCooldown = DateTime.Now; Task engineThinkTask; @@ -102,6 +105,11 @@ protected void OnLoadFEN (object sender, EventArgs e) } MainClass.ResetClock (); UpdateMaterialDifference (MainClass.CurrentBoard); + MainClass.CurrentGameHistory = new GameHistory (); + ClearGameHistoryView (); + if (MainClass.CurrentBoard.PlayerToMove == PieceColour.Black) { + GameHistoryView.Buffer.Text = "1. ... "; + } } protected void OnLoadEngineOne (object sender, EventArgs e) @@ -173,6 +181,18 @@ public void ShowGameOverDialog(GameStatus status) message = "Time expired: Black. White wins."; MainClass.CurrentGameStatus = GameStatus.BlackTime; break; + case GameStatus.DrawInsuffientMaterial: + message = "Game is a draw due to insufficient material."; + MainClass.CurrentGameStatus = GameStatus.DrawInsuffientMaterial; + break; + case GameStatus.DrawFifty: + message = "Game is a draw according to the 50-move rule."; + MainClass.CurrentGameStatus = GameStatus.DrawFifty; + break; + case GameStatus.DrawRepetition: + message = "Game is a draw by threefold repetition."; + MainClass.CurrentGameStatus = GameStatus.DrawRepetition; + break; default: break; } @@ -223,6 +243,8 @@ protected void OnResetBoard (object sender, EventArgs e) MainClass.ResetClock (); PiecePseudoLegalMoves.GeneratePseudoLegalMoves (MainClass.CurrentBoard); PieceLegalMoves.GenerateLegalMoves (MainClass.CurrentBoard); + MainClass.CurrentGameHistory = new GameHistory (); + ClearGameHistoryView (); RedrawBoard (); } @@ -322,6 +344,10 @@ void EngineTwoMove() private void ParseAndMakeMove(string move, int engine) { + if (move.Length < 4) + { + return; + } string sourceStr = move.Substring (0, 2); string destinationStr = move.Substring (2, 2); string promoteToStr = ""; @@ -350,24 +376,74 @@ private void ParseAndMakeMove(string move, int engine) } try { - MainClass.CurrentBoard.MakeMove (sourceByte, destinationByte, promoteTo); + SpecifierType specifierRequired = GameHistory.checkDisabiguationNeeded(MainClass.CurrentBoard, sourceByte, destinationByte); + MoveResult result = MainClass.CurrentBoard.MakeMove (sourceByte, destinationByte, promoteTo); + + Piece movingPiece = null; + if(promoteTo == null) { + movingPiece = MainClass.CurrentBoard.Squares[destinationByte].Piece; + } else { + movingPiece = new Piece(MainClass.CurrentBoard.Squares [destinationByte].Piece.Colour, PieceType.Pawn); + } + + int checkOrCheckmate = 0; + GameStatus mateState = MainClass.CurrentBoard.CheckForMate (); + if (mateState == GameStatus.WhiteCheckmate || mateState == GameStatus.BlackCheckmate) { + checkOrCheckmate = 2; + } else if (MainClass.CurrentBoard.WhiteCheck || MainClass.CurrentBoard.BlackCheck) { + checkOrCheckmate = 1; + } + + if(result == MoveResult.Capture && movingPiece.Type == PieceType.Pawn) { + specifierRequired = SpecifierType.File; + } + + string fenPosition = MainClass.CurrentBoard.ToFEN().Split(' ')[0]; + MainClass.CurrentGameHistory.AddMove(new Move(sourceByte, destinationByte, + MainClass.CurrentBoard.Squares [destinationByte].Piece.Colour, + movingPiece, + result, + MainClass.CurrentBoard.ToFEN(), + checkOrCheckmate, + specifierRequired, + promoteTo), fenPosition); + Gtk.Application.Invoke(delegate { + UpdateGameHistoryView(); + }); + + if (MainClass.CurrentGameHistory.UpdateFiftyMoveCount (result) == GameStatus.DrawFifty) { + MainClass.CurrentGameStatus = GameStatus.DrawFifty; + } else if (MainClass.CurrentGameHistory.CheckThreefoldRepetition() == GameStatus.DrawRepetition) { + MainClass.CurrentGameStatus = GameStatus.DrawRepetition; + } + Gtk.Application.Invoke(delegate { RedrawBoard(); }); } catch(InvalidOperationException) { throw new InvalidOperationException (move); } - MainClass.CurrentGameStatus = MainClass.CurrentBoard.CheckForMate (); + + GameStatus isMate = MainClass.CurrentBoard.CheckForMate (); + if (isMate != GameStatus.Active) { + MainClass.CurrentGameStatus = isMate; + } if (MainClass.CurrentGameStatus != GameStatus.Active && MainClass.CurrentGameStatus != GameStatus.Inactive) { Gtk.Application.Invoke(delegate { ShowGameOverDialog(MainClass.CurrentGameStatus); }); } + + if (MainClass.CurrentGameStatus == GameStatus.Inactive) { + MainClass.CurrentGameStatus = GameStatus.Active; + } + Gtk.Application.Invoke (delegate { MainClass.UpdateClock (); UpdatePlayerToMove(); }); - if (MainClass.CurrentMode == GameMode.Engines) { + + if (MainClass.CurrentMode == GameMode.Engines && MainClass.CurrentGameStatus == GameStatus.Active) { Thread.Sleep (1500); if (engine == 1) { Gtk.Application.Invoke (delegate { @@ -549,6 +625,14 @@ protected void OnPieceClick (object o, ButtonPressEventArgs args) MainClass.EngineTwo.StopAndIgnoreMove (); } + if (!MainClass.CurrentBoard.IsMoveValid (selectedPiece, (byte)pieceIndex)) { + currentSelectionState = PieceSelectionState.None; + Gtk.Application.Invoke (delegate { + RedrawBoard (); + }); + return; + } + if (MainClass.CurrentGameStatus != GameStatus.Active && MainClass.CurrentGameStatus != GameStatus.Inactive) { Console.Error.WriteLine ("(EE) Attempted move during finished game."); @@ -579,17 +663,62 @@ protected void OnPieceClick (object o, ButtonPressEventArgs args) } try { - MainClass.CurrentBoard.MakeMove (selectedPiece, (byte)pieceIndex, promoteTo); + SpecifierType specifierRequired = GameHistory.checkDisabiguationNeeded(MainClass.CurrentBoard, selectedPiece, (byte)pieceIndex); + MoveResult result = MainClass.CurrentBoard.MakeMove (selectedPiece, (byte)pieceIndex, promoteTo); + + Piece movingPiece = null; + if(promoteTo == null) { + movingPiece = MainClass.CurrentBoard.Squares[(byte)pieceIndex].Piece; + } else { + movingPiece = new Piece(MainClass.CurrentBoard.Squares [(byte)pieceIndex].Piece.Colour, PieceType.Pawn); + } + + if(result == MoveResult.Capture && movingPiece.Type == PieceType.Pawn) { + specifierRequired = SpecifierType.File; + } + + int checkOrCheckmate = 0; + GameStatus mateState = MainClass.CurrentBoard.CheckForMate (); + if (mateState == GameStatus.WhiteCheckmate || mateState == GameStatus.BlackCheckmate) { + checkOrCheckmate = 2; + } else if (MainClass.CurrentBoard.WhiteCheck || MainClass.CurrentBoard.BlackCheck) { + checkOrCheckmate = 1; + } + + string fenPosition = MainClass.CurrentBoard.ToFEN().Split(' ')[0]; + MainClass.CurrentGameHistory.AddMove(new Move(selectedPiece, (byte)pieceIndex, + MainClass.CurrentBoard.Squares [(byte)pieceIndex].Piece.Colour, + movingPiece, + result, + MainClass.CurrentBoard.ToFEN(), + checkOrCheckmate, + specifierRequired, + promoteTo), fenPosition); + UpdateGameHistoryView(); + + if (MainClass.CurrentGameHistory.UpdateFiftyMoveCount (result) == GameStatus.DrawFifty) { + MainClass.CurrentGameStatus = GameStatus.DrawFifty; + } else if (MainClass.CurrentGameHistory.CheckThreefoldRepetition() == GameStatus.DrawRepetition) { + MainClass.CurrentGameStatus = GameStatus.DrawRepetition; + } } catch(InvalidOperationException) { Debug.Log ("Invalid move entered."); } Gtk.Application.Invoke(delegate { RedrawBoard(); }); - MainClass.CurrentGameStatus = MainClass.CurrentBoard.CheckForMate (); + GameStatus isMate = MainClass.CurrentBoard.CheckForMate (); + if (isMate != GameStatus.Active) { + MainClass.CurrentGameStatus = isMate; + } if (MainClass.CurrentGameStatus != GameStatus.Active && MainClass.CurrentGameStatus != GameStatus.Inactive) { ShowGameOverDialog (MainClass.CurrentGameStatus); } + + if (MainClass.CurrentGameStatus == GameStatus.Inactive) { + MainClass.CurrentGameStatus = GameStatus.Active; + } + Gtk.Application.Invoke (delegate { MainClass.UpdateClock (); UpdatePlayerToMove(); @@ -598,7 +727,7 @@ protected void OnPieceClick (object o, ButtonPressEventArgs args) currentSelectionState = PieceSelectionState.None; if (MainClass.EngineOne != null && MainClass.CurrentMode == GameMode.OnePlayer) - EngineTwoMove (); + EngineOneMove (); } } @@ -608,12 +737,17 @@ protected void OnPieceClick (object o, ButtonPressEventArgs args) */ public void UpdateMaterialDifference(Board board) { + int totalWhite = 0; + int totalBlack = 0; + int materialDifference = 0; foreach (Square sq in board.Squares) { if (sq.Piece != null) { if (sq.Piece.Type != PieceType.King && sq.Piece.Colour == PieceColour.White) { + totalWhite += pieceValues [(int)(sq.Piece.Type)]; materialDifference += pieceValues [(int)(sq.Piece.Type)]; } else if (sq.Piece.Type != PieceType.King && sq.Piece.Colour == PieceColour.Black) { + totalBlack += pieceValues [(int)(sq.Piece.Type)]; materialDifference -= pieceValues [(int)(sq.Piece.Type)]; } } @@ -628,6 +762,35 @@ public void UpdateMaterialDifference(Board board) } else { MaterialDifferenceLabel.Text = "Material is equal."; } + + int maxMaterial = Math.Max (totalWhite, totalBlack); + if (maxMaterial <= 3) { // We compare to 3 because kings are not counted. + int minMaterial = Math.Min (totalWhite, totalBlack); + if (minMaterial == 0) { + MainClass.CurrentGameStatus = GameStatus.DrawInsuffientMaterial; + } else if (maxMaterial == minMaterial && maxMaterial == 3) { // Now we check for opposite-coloured bishops + int whiteBishopSquare = -1; + int blackBishopSquare = -1; + for(int i = 0; i < board.Squares.Length; i++) { + Square sq = board.Squares [i]; + if (sq.Piece != null && sq.Piece.Type == PieceType.Bishop) { + if (sq.Piece.Colour == PieceColour.White) + whiteBishopSquare = i; + else + blackBishopSquare = i; + } + } + // Only continue if both pieces are actually bishops + if (whiteBishopSquare != -1 && blackBishopSquare != -1) { + // Now we check to see if they are on opposite colours, using XOR + bool whiteBishopOnWhiteSquare = Array.IndexOf (whiteSquares, (byte)whiteBishopSquare) > -1; + bool blackBishopOnWhiteSquare = Array.IndexOf (whiteSquares, (byte)blackBishopSquare) > -1; + if (whiteBishopOnWhiteSquare ^ blackBishopOnWhiteSquare) { + MainClass.CurrentGameStatus = GameStatus.DrawInsuffientMaterial; + } + } + } + } } protected void OnSetEngineStrength (object sender, EventArgs e) @@ -772,5 +935,73 @@ protected void OnLoadEngineTwo (object sender, EventArgs e) } chooser.Destroy (); } + + protected void OnExportPGN (object sender, EventArgs e) + { + var fc = new FileChooserDialog ("Choose where to save the PGN", + this, + FileChooserAction.Save, + "Cancel", ResponseType.Cancel, + "Save", ResponseType.Accept); + + if (fc.Run () == (int)ResponseType.Accept) { + MainClass.CurrentGameHistory.SavePGN (fc.Filename); + } + fc.Destroy (); + } + + private void UpdateGameHistoryView() + { + Move move = MainClass.CurrentGameHistory.GetLastMove (); + string moveOutput = ""; + if (move.Colour == PieceColour.White) { + moveOutput += " " + MainClass.CurrentGameHistory.MoveCount + ". "; + } + moveOutput += GameHistory.MoveToNotation (move) + " "; + GameHistoryView.Buffer.Text += moveOutput; + } + + private void ClearGameHistoryView() + { + GameHistoryView.Buffer.Text = ""; + } + + protected void OnImportPGN (object sender, EventArgs e) + { + var fc = new FileChooserDialog ("Choose a PGN file to open.", + this, + FileChooserAction.Open, + "Cancel", ResponseType.Cancel, + "Open", ResponseType.Accept); + if (fc.Run () == (int)ResponseType.Accept) { + MainClass.CurrentGameHistory = GameHistory.importPGN (File.ReadAllText (fc.Filename)); + string pgn = MainClass.CurrentGameHistory.ToPGNString (); + int indexOfMovesStart = pgn.IndexOf ("1."); + if (indexOfMovesStart > 0) { + GameHistoryView.Buffer.Text = pgn.Substring (indexOfMovesStart); + } + + // Load the FEN from the last move + FENParser parser = new FENParser(MainClass.CurrentGameHistory.GetLastMove().FEN); + MainClass.CurrentBoard = parser.GetBoard(); + MainClass.CurrentGameStatus = GameStatus.Inactive; + PiecePseudoLegalMoves.GeneratePseudoLegalMoves(MainClass.CurrentBoard); + PieceLegalMoves.GenerateLegalMoves(MainClass.CurrentBoard); + Gtk.Application.Invoke (delegate { + RedrawBoard (); + }); + + MainClass.CurrentGameStatus = GameStatus.Inactive; + GameStatus currentStatus = MainClass.CurrentBoard.CheckForMate (); + if (currentStatus != GameStatus.Inactive && currentStatus != GameStatus.Active) { + Gtk.Application.Invoke (delegate { + ShowGameOverDialog (currentStatus); + }); + } + MainClass.ResetClock (); + UpdateMaterialDifference (MainClass.CurrentBoard); + } + fc.Destroy (); + } } } diff --git a/gui/GUI/PiecePseudoLegalMoves.cs b/gui/GUI/PiecePseudoLegalMoves.cs index a99f133..cf59f7d 100644 --- a/gui/GUI/PiecePseudoLegalMoves.cs +++ b/gui/GUI/PiecePseudoLegalMoves.cs @@ -101,7 +101,17 @@ private static void CheckPseudoLegalMovesPawn(List moves, Piece movingPiec private static void CheckValidAttackPawn(Board board, byte destination, Piece movingPiece) { - // TODO: Add support for en passant. + // Handle en passant + if (board.EnPassantSquare > -1 && movingPiece.Colour != board.EnPassantColour && destination == board.EnPassantSquare) { + // We have a valid en passant possible + movingPiece.PseudoLegalMoves.Add (destination); + + if (movingPiece.Colour == PieceColour.White) { + WhiteAttackBoard [destination] = true; + } else { + BlackAttackBoard [destination] = true; + } + } Piece attackedPiece = board.Squares [destination].Piece; diff --git a/gui/GUI/Program.cs b/gui/GUI/Program.cs index fb754b8..14b580c 100644 --- a/gui/GUI/Program.cs +++ b/gui/GUI/Program.cs @@ -20,6 +20,7 @@ class MainClass public static StrengthMeasure StrengthType { get; set; } public static int StrengthValue { get; set; } public static GameMode CurrentMode { get; set; } + public static GameHistory CurrentGameHistory { get; set; } public static void Main (string[] args) { @@ -35,6 +36,7 @@ public static void Main (string[] args) StrengthType = StrengthMeasure.Depth; StrengthValue = 7; CurrentMode = GameMode.OnePlayer; + CurrentGameHistory = new GameHistory (); Application.Init (); win = new MainWindow (); diff --git a/gui/GUI/UCITransceiver.cs b/gui/GUI/UCITransceiver.cs index 149cf2d..484812a 100644 --- a/gui/GUI/UCITransceiver.cs +++ b/gui/GUI/UCITransceiver.cs @@ -97,10 +97,11 @@ public string Go(string time = "infinite") { }); if (response.StartsWith ("bestmove")) { IsThinking = false; - if(response.Substring(9).Length > 4) { - return response.Substring (9, 5); + string[] responseTokens = response.Split(' '); + if (responseTokens.Length > 1) { + return responseTokens[1]; } else { - return response.Substring (9, 4); + return ""; } } } while(true); @@ -117,7 +118,15 @@ public string StopAndGetBestMove() if (response.StartsWith ("bestmove")) { WaitUntilReady(); Debug.Log ("Engine stopped and ready for new input."); - return response.Substring (9, 5); + string[] responseTokens = response.Split(' '); + if (responseTokens.Length > 1) + { + return responseTokens[1]; + } + else + { + return ""; + } } } while(response != null); throw new TimeoutException ("Engine has stopped responding."); diff --git a/gui/GUI/gtk-gui/GUI.EngineStrengthDialog.cs b/gui/GUI/gtk-gui/GUI.EngineStrengthDialog.cs index 3f34ba1..8a54152 100644 --- a/gui/GUI/gtk-gui/GUI.EngineStrengthDialog.cs +++ b/gui/GUI/gtk-gui/GUI.EngineStrengthDialog.cs @@ -43,6 +43,7 @@ protected virtual void Build () this.DepthButton = new global::Gtk.RadioButton (global::Mono.Unix.Catalog.GetString ("Depth")); this.DepthButton.CanFocus = true; this.DepthButton.Name = "DepthButton"; + this.DepthButton.Active = true; this.DepthButton.DrawIndicator = true; this.DepthButton.UseUnderline = true; this.DepthButton.Group = new global::GLib.SList (global::System.IntPtr.Zero); diff --git a/gui/GUI/gtk-gui/GUI.MainWindow.cs b/gui/GUI/gtk-gui/GUI.MainWindow.cs index ad4b74a..b6fff24 100644 --- a/gui/GUI/gtk-gui/GUI.MainWindow.cs +++ b/gui/GUI/gtk-gui/GUI.MainWindow.cs @@ -48,6 +48,12 @@ public partial class MainWindow private global::Gtk.Action LoadEngine2Action; + private global::Gtk.Action PGNAction; + + private global::Gtk.Action ExportAction; + + private global::Gtk.Action ImportAction; + private global::Gtk.VBox vbox1; private global::Gtk.MenuBar MenuBar; @@ -78,6 +84,8 @@ public partial class MainWindow private global::Gtk.Label PlayerToMoveLabel; + private global::Gtk.TextView GameHistoryView; + private global::Gtk.HBox hbox1; private global::Gtk.Label EngineOneNameLabel; @@ -174,6 +182,15 @@ protected virtual void Build () this.LoadEngine2Action = new global::Gtk.Action ("LoadEngine2Action", global::Mono.Unix.Catalog.GetString ("Load Engine 2"), null, null); this.LoadEngine2Action.ShortLabel = global::Mono.Unix.Catalog.GetString ("Load Engine 2"); w1.Add (this.LoadEngine2Action, "2"); + this.PGNAction = new global::Gtk.Action ("PGNAction", global::Mono.Unix.Catalog.GetString ("PGN"), null, null); + this.PGNAction.ShortLabel = global::Mono.Unix.Catalog.GetString ("PGN"); + w1.Add (this.PGNAction, null); + this.ExportAction = new global::Gtk.Action ("ExportAction", global::Mono.Unix.Catalog.GetString ("Export..."), null, null); + this.ExportAction.ShortLabel = global::Mono.Unix.Catalog.GetString ("Export..."); + w1.Add (this.ExportAction, null); + this.ImportAction = new global::Gtk.Action ("ImportAction", global::Mono.Unix.Catalog.GetString ("Import..."), null, null); + this.ImportAction.ShortLabel = global::Mono.Unix.Catalog.GetString ("Import..."); + w1.Add (this.ImportAction, null); this.UIManager.InsertActionGroup (w1, 0); this.AddAccelGroup (this.UIManager.AccelGroup); this.Name = "GUI.MainWindow"; @@ -184,7 +201,7 @@ protected virtual void Build () this.vbox1.Name = "vbox1"; this.vbox1.Spacing = 6; // Container child vbox1.Gtk.Box+BoxChild - this.UIManager.AddUiFromString (""); + this.UIManager.AddUiFromString (""); this.MenuBar = ((global::Gtk.MenuBar)(this.UIManager.GetWidget ("/MenuBar"))); this.MenuBar.Name = "MenuBar"; this.vbox1.Add (this.MenuBar); @@ -213,12 +230,13 @@ protected virtual void Build () w4.Position = 0; // Container child hbox3.Gtk.Box+BoxChild this.vbox3 = new global::Gtk.VBox (); - this.vbox3.WidthRequest = 200; + this.vbox3.WidthRequest = 400; this.vbox3.Name = "vbox3"; this.vbox3.Spacing = 6; // Container child vbox3.Gtk.Box+BoxChild this.hbox4 = new global::Gtk.HBox (); this.hbox4.Name = "hbox4"; + this.hbox4.Homogeneous = true; this.hbox4.Spacing = 6; // Container child hbox4.Gtk.Box+BoxChild this.vbox4 = new global::Gtk.VBox (); @@ -305,12 +323,23 @@ protected virtual void Build () w13.Position = 2; w13.Expand = false; w13.Fill = false; + // Container child vbox3.Gtk.Box+BoxChild + this.GameHistoryView = new global::Gtk.TextView (); + this.GameHistoryView.WidthRequest = 400; + this.GameHistoryView.CanFocus = true; + this.GameHistoryView.Name = "GameHistoryView"; + this.GameHistoryView.Editable = false; + this.GameHistoryView.CursorVisible = false; + this.GameHistoryView.WrapMode = ((global::Gtk.WrapMode)(2)); + this.vbox3.Add (this.GameHistoryView); + global::Gtk.Box.BoxChild w14 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.GameHistoryView])); + w14.Position = 3; this.hbox3.Add (this.vbox3); - global::Gtk.Box.BoxChild w14 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.vbox3])); - w14.Position = 1; - this.vbox1.Add (this.hbox3); - global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.hbox3])); + global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.vbox3])); w15.Position = 1; + this.vbox1.Add (this.hbox3); + global::Gtk.Box.BoxChild w16 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.hbox3])); + w16.Position = 1; // Container child vbox1.Gtk.Box+BoxChild this.hbox1 = new global::Gtk.HBox (); this.hbox1.Name = "hbox1"; @@ -321,39 +350,39 @@ protected virtual void Build () this.EngineOneNameLabel.Xpad = 10; this.EngineOneNameLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("engine_name"); this.hbox1.Add (this.EngineOneNameLabel); - global::Gtk.Box.BoxChild w16 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.EngineOneNameLabel])); - w16.Position = 0; - w16.Expand = false; - w16.Fill = false; + global::Gtk.Box.BoxChild w17 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.EngineOneNameLabel])); + w17.Position = 0; + w17.Expand = false; + w17.Fill = false; // Container child hbox1.Gtk.Box+BoxChild this.EngineOneAuthorLabel = new global::Gtk.Label (); this.EngineOneAuthorLabel.Name = "EngineOneAuthorLabel"; this.EngineOneAuthorLabel.Xpad = 10; this.EngineOneAuthorLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("engine_author"); this.hbox1.Add (this.EngineOneAuthorLabel); - global::Gtk.Box.BoxChild w17 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.EngineOneAuthorLabel])); - w17.Position = 1; + global::Gtk.Box.BoxChild w18 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.EngineOneAuthorLabel])); + w18.Position = 1; // Container child hbox1.Gtk.Box+BoxChild this.EngineOneDepthLabel = new global::Gtk.Label (); this.EngineOneDepthLabel.Name = "EngineOneDepthLabel"; this.EngineOneDepthLabel.Xpad = 10; this.EngineOneDepthLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("engine_depth"); this.hbox1.Add (this.EngineOneDepthLabel); - global::Gtk.Box.BoxChild w18 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.EngineOneDepthLabel])); - w18.Position = 2; + global::Gtk.Box.BoxChild w19 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.EngineOneDepthLabel])); + w19.Position = 2; // Container child hbox1.Gtk.Box+BoxChild this.EngineOneNPSLabel = new global::Gtk.Label (); this.EngineOneNPSLabel.Name = "EngineOneNPSLabel"; this.EngineOneNPSLabel.Xpad = 10; this.EngineOneNPSLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("engine_nps"); this.hbox1.Add (this.EngineOneNPSLabel); - global::Gtk.Box.BoxChild w19 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.EngineOneNPSLabel])); - w19.Position = 3; + global::Gtk.Box.BoxChild w20 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.EngineOneNPSLabel])); + w20.Position = 3; this.vbox1.Add (this.hbox1); - global::Gtk.Box.BoxChild w20 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.hbox1])); - w20.Position = 2; - w20.Expand = false; - w20.Fill = false; + global::Gtk.Box.BoxChild w21 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.hbox1])); + w21.Position = 2; + w21.Expand = false; + w21.Fill = false; // Container child vbox1.Gtk.Box+BoxChild this.hbox2 = new global::Gtk.HBox (); this.hbox2.Name = "hbox2"; @@ -364,40 +393,40 @@ protected virtual void Build () this.EngineTwoNameLabel.Xpad = 10; this.EngineTwoNameLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("engine_name"); this.hbox2.Add (this.EngineTwoNameLabel); - global::Gtk.Box.BoxChild w21 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.EngineTwoNameLabel])); - w21.Position = 0; - w21.Expand = false; - w21.Fill = false; + global::Gtk.Box.BoxChild w22 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.EngineTwoNameLabel])); + w22.Position = 0; + w22.Expand = false; + w22.Fill = false; // Container child hbox2.Gtk.Box+BoxChild this.EngineTwoAuthorLabel = new global::Gtk.Label (); this.EngineTwoAuthorLabel.Name = "EngineTwoAuthorLabel"; this.EngineTwoAuthorLabel.Xpad = 10; this.EngineTwoAuthorLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("engine_author"); this.hbox2.Add (this.EngineTwoAuthorLabel); - global::Gtk.Box.BoxChild w22 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.EngineTwoAuthorLabel])); - w22.Position = 1; + global::Gtk.Box.BoxChild w23 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.EngineTwoAuthorLabel])); + w23.Position = 1; // Container child hbox2.Gtk.Box+BoxChild this.EngineTwoDepthLabel = new global::Gtk.Label (); this.EngineTwoDepthLabel.Name = "EngineTwoDepthLabel"; this.EngineTwoDepthLabel.Xpad = 10; this.EngineTwoDepthLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("engine_depth"); this.hbox2.Add (this.EngineTwoDepthLabel); - global::Gtk.Box.BoxChild w23 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.EngineTwoDepthLabel])); - w23.Position = 2; + global::Gtk.Box.BoxChild w24 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.EngineTwoDepthLabel])); + w24.Position = 2; // Container child hbox2.Gtk.Box+BoxChild this.EngineTwoNPSLabel = new global::Gtk.Label (); this.EngineTwoNPSLabel.Name = "EngineTwoNPSLabel"; this.EngineTwoNPSLabel.Xpad = 10; this.EngineTwoNPSLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("engine_nps"); this.hbox2.Add (this.EngineTwoNPSLabel); - global::Gtk.Box.BoxChild w24 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.EngineTwoNPSLabel])); - w24.Position = 3; - this.vbox1.Add (this.hbox2); - global::Gtk.Box.BoxChild w25 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.hbox2])); + global::Gtk.Box.BoxChild w25 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.EngineTwoNPSLabel])); w25.Position = 3; - w25.Expand = false; - w25.Fill = false; - w25.Padding = ((uint)(2)); + this.vbox1.Add (this.hbox2); + global::Gtk.Box.BoxChild w26 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.hbox2])); + w26.Position = 3; + w26.Expand = false; + w26.Fill = false; + w26.Padding = ((uint)(2)); // Container child vbox1.Gtk.Box+BoxChild this.GtkScrolledWindow = new global::Gtk.ScrolledWindow (); this.GtkScrolledWindow.Name = "GtkScrolledWindow"; @@ -413,14 +442,14 @@ protected virtual void Build () this.EngineOutput.LeftMargin = 5; this.GtkScrolledWindow.Add (this.EngineOutput); this.vbox1.Add (this.GtkScrolledWindow); - global::Gtk.Box.BoxChild w27 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.GtkScrolledWindow])); - w27.Position = 4; + global::Gtk.Box.BoxChild w28 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.GtkScrolledWindow])); + w28.Position = 4; this.Add (this.vbox1); if ((this.Child != null)) { this.Child.ShowAll (); } - this.DefaultWidth = 773; - this.DefaultHeight = 705; + this.DefaultWidth = 973; + this.DefaultHeight = 715; this.Show (); this.DeleteEvent += new global::Gtk.DeleteEventHandler (this.OnDeleteEvent); this.QuitAction.Activated += new global::System.EventHandler (this.OnQuit); @@ -437,6 +466,8 @@ protected virtual void Build () this.EnginesAction.Activated += new global::System.EventHandler (this.OnEnginesSet); this.AnalysePositionAction.Activated += new global::System.EventHandler (this.OnAnalyseMove); this.LoadEngine2Action.Activated += new global::System.EventHandler (this.OnLoadEngineTwo); + this.ExportAction.Activated += new global::System.EventHandler (this.OnExportPGN); + this.ImportAction.Activated += new global::System.EventHandler (this.OnImportPGN); this.BoardArea.ExposeEvent += new global::Gtk.ExposeEventHandler (this.OnBoardExpose); this.BoardArea.ButtonPressEvent += new global::Gtk.ButtonPressEventHandler (this.OnPieceClick); } diff --git a/gui/GUI/gtk-gui/GUI.PawnPromotionDialog.cs b/gui/GUI/gtk-gui/GUI.PawnPromotionDialog.cs index bf19d87..eb642d5 100644 --- a/gui/GUI/gtk-gui/GUI.PawnPromotionDialog.cs +++ b/gui/GUI/gtk-gui/GUI.PawnPromotionDialog.cs @@ -29,7 +29,6 @@ protected virtual void Build () this.WidthRequest = 350; this.HeightRequest = 125; this.Name = "GUI.PawnPromotionDialog"; - this.Title = global::Mono.Unix.Catalog.GetString ("dialog1"); this.WindowPosition = ((global::Gtk.WindowPosition)(4)); this.Resizable = false; this.AllowGrow = false; diff --git a/gui/GUI/gtk-gui/gui.stetic b/gui/GUI/gtk-gui/gui.stetic index c58fb18..4359cb4 100644 --- a/gui/GUI/gtk-gui/gui.stetic +++ b/gui/GUI/gtk-gui/gui.stetic @@ -8,7 +8,7 @@ - + Action @@ -153,6 +153,23 @@ Load Engine 2 + + Action + PGN + PGN + + + Action + Export... + Export... + + + + Action + Import... + Import... + + Gandalf Chess GUI @@ -176,6 +193,10 @@ + + + + @@ -231,11 +252,12 @@ - 200 + 400 6 + True 6 @@ -317,7 +339,7 @@ 0 - True + False False False @@ -350,7 +372,19 @@ - + + + 400 + True + False + False + + Word + + + 3 + True +