Initial Git commit
authorJerome St-Louis <jerome@ecere.com>
Tue, 17 May 2011 04:22:20 +0000 (00:22 -0400)
committerJerome St-Louis <jerome@ecere.com>
Tue, 17 May 2011 04:22:20 +0000 (00:22 -0400)
28 files changed:
LICENSE [new file with mode: 0644]
chess.epj [new file with mode: 0644]
res/aboutPic.jpg [new file with mode: 0644]
res/blackBishop.png [new file with mode: 0644]
res/blackKing.png [new file with mode: 0644]
res/blackKnight.png [new file with mode: 0644]
res/blackPawn.png [new file with mode: 0644]
res/blackQueen.png [new file with mode: 0644]
res/blackRook.png [new file with mode: 0644]
res/board.jpg [new file with mode: 0644]
res/bthr.jpg [new file with mode: 0644]
res/chessSet.3ds [new file with mode: 0644]
res/darkwood.jpg [new file with mode: 0644]
res/lightwo1.jpg [new file with mode: 0644]
res/whiteBishop.png [new file with mode: 0644]
res/whiteKing.png [new file with mode: 0644]
res/whiteKnight.png [new file with mode: 0644]
res/whitePawn.png [new file with mode: 0644]
res/whiteQueen.png [new file with mode: 0644]
res/whiteRook.png [new file with mode: 0644]
src/about.ec [new file with mode: 0644]
src/ai.ec [new file with mode: 0644]
src/chess.ec [new file with mode: 0644]
src/chess2D.ec [new file with mode: 0644]
src/chess3D.ec [new file with mode: 0644]
src/chessutils.ec [new file with mode: 0644]
src/connect.ec [new file with mode: 0644]
src/promotion.ec [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..61b4682
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+   Copyright (c) 1996-2007, Jerome Jacovella-St-Louis
+   Copyright (c) 2005-2007, Ecere Corporation
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of Ecere Corporation nor the names of its contributors
+      may be used to endorse or promote products derived from this software 
+      without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/chess.epj b/chess.epj
new file mode 100644 (file)
index 0000000..843f10f
--- /dev/null
+++ b/chess.epj
@@ -0,0 +1,70 @@
+{
+   "Version" : 0.2,
+   "ModuleName" : "chess",
+   "Options" : {
+      "TargetType" : "Executable",
+      "TargetFileName" : "chess",
+      "Libraries" : [
+         "ecere"
+      ]
+   },
+   "Configurations" : [
+      {
+         "Name" : "Debug",
+         "Options" : {
+            "Debug" : true,
+            "Console" : true
+         }
+      },
+      {
+         "Name" : "Release",
+         "Options" : {
+            "Optimization" : "Speed"
+         }
+      },
+      {
+         "Name" : "MemoryGuard",
+         "Options" : {
+            "Debug" : true,
+            "MemoryGuard" : true,
+            "Console" : true
+         }
+      }
+   ],
+   "Files" : [
+      {
+         "Folder" : "src",
+         "Files" : [
+            "about.ec",
+            "ai.ec",
+            "chess.ec",
+            "chess2D.ec",
+            "chess3D.ec",
+            "chessutils.ec",
+            "connect.ec",
+            "promotion.ec"
+         ]
+      }
+   ],
+   "ResourcesPath" : "res",
+   "Resources" : [
+      "aboutPic.jpg",
+      "blackBishop.png",
+      "blackKing.png",
+      "blackKnight.png",
+      "blackPawn.png",
+      "blackQueen.png",
+      "blackRook.png",
+      "board.jpg",
+      "bthr.jpg",
+      "darkwood.jpg",
+      "lightwo1.jpg",
+      "whiteBishop.png",
+      "whiteKing.png",
+      "whiteKnight.png",
+      "whitePawn.png",
+      "whiteQueen.png",
+      "whiteRook.png",
+      "chessSet.3ds"
+   ]
+}
\ No newline at end of file
diff --git a/res/aboutPic.jpg b/res/aboutPic.jpg
new file mode 100644 (file)
index 0000000..6a0a1b3
Binary files /dev/null and b/res/aboutPic.jpg differ
diff --git a/res/blackBishop.png b/res/blackBishop.png
new file mode 100644 (file)
index 0000000..f457c92
Binary files /dev/null and b/res/blackBishop.png differ
diff --git a/res/blackKing.png b/res/blackKing.png
new file mode 100644 (file)
index 0000000..b621783
Binary files /dev/null and b/res/blackKing.png differ
diff --git a/res/blackKnight.png b/res/blackKnight.png
new file mode 100644 (file)
index 0000000..85b3c2a
Binary files /dev/null and b/res/blackKnight.png differ
diff --git a/res/blackPawn.png b/res/blackPawn.png
new file mode 100644 (file)
index 0000000..037ea60
Binary files /dev/null and b/res/blackPawn.png differ
diff --git a/res/blackQueen.png b/res/blackQueen.png
new file mode 100644 (file)
index 0000000..408cb2f
Binary files /dev/null and b/res/blackQueen.png differ
diff --git a/res/blackRook.png b/res/blackRook.png
new file mode 100644 (file)
index 0000000..4ddbd60
Binary files /dev/null and b/res/blackRook.png differ
diff --git a/res/board.jpg b/res/board.jpg
new file mode 100644 (file)
index 0000000..bb755bb
Binary files /dev/null and b/res/board.jpg differ
diff --git a/res/bthr.jpg b/res/bthr.jpg
new file mode 100644 (file)
index 0000000..56dff58
Binary files /dev/null and b/res/bthr.jpg differ
diff --git a/res/chessSet.3ds b/res/chessSet.3ds
new file mode 100644 (file)
index 0000000..c69a86a
Binary files /dev/null and b/res/chessSet.3ds differ
diff --git a/res/darkwood.jpg b/res/darkwood.jpg
new file mode 100644 (file)
index 0000000..8853e37
Binary files /dev/null and b/res/darkwood.jpg differ
diff --git a/res/lightwo1.jpg b/res/lightwo1.jpg
new file mode 100644 (file)
index 0000000..437e05d
Binary files /dev/null and b/res/lightwo1.jpg differ
diff --git a/res/whiteBishop.png b/res/whiteBishop.png
new file mode 100644 (file)
index 0000000..75eb4ec
Binary files /dev/null and b/res/whiteBishop.png differ
diff --git a/res/whiteKing.png b/res/whiteKing.png
new file mode 100644 (file)
index 0000000..0326b3a
Binary files /dev/null and b/res/whiteKing.png differ
diff --git a/res/whiteKnight.png b/res/whiteKnight.png
new file mode 100644 (file)
index 0000000..8e0062d
Binary files /dev/null and b/res/whiteKnight.png differ
diff --git a/res/whitePawn.png b/res/whitePawn.png
new file mode 100644 (file)
index 0000000..5695681
Binary files /dev/null and b/res/whitePawn.png differ
diff --git a/res/whiteQueen.png b/res/whiteQueen.png
new file mode 100644 (file)
index 0000000..5e2849f
Binary files /dev/null and b/res/whiteQueen.png differ
diff --git a/res/whiteRook.png b/res/whiteRook.png
new file mode 100644 (file)
index 0000000..1a2b3d2
Binary files /dev/null and b/res/whiteRook.png differ
diff --git a/src/about.ec b/src/about.ec
new file mode 100644 (file)
index 0000000..aeee025
--- /dev/null
@@ -0,0 +1,30 @@
+import "chess.ec"
+
+class AboutChess : Window
+{
+   background = black; 
+   foreground = white, size = Size { 440, 200 }, hasClose = true,
+   text = APPNAME;
+
+   Button ok
+   {
+      this, isDefault = true, text = "OK", 
+      anchor = { bottom = 10 }, size = { 80 };
+
+      NotifyClicked = ButtonCloseDialog;
+   };
+
+   Picture picture
+   {
+      this, position = { 0, 10 }, image = { ":aboutPic.jpg" },
+      size = { 180, 130 }
+   };
+
+   void OnRedraw(Surface surface)
+   {
+      surface.WriteTextf(200, 30, "Copyright (c) 1996-2005");
+      surface.WriteTextf(200, 50, "   Jerome Jacovella-St-Louis");
+      surface.WriteTextf(200, 70, "Models Copyright (c) 2004");
+      surface.WriteTextf(200, 90, "   Gaetan Loyer");
+   }
+}
diff --git a/src/ai.ec b/src/ai.ec
new file mode 100644 (file)
index 0000000..2e4bcd4
--- /dev/null
+++ b/src/ai.ec
@@ -0,0 +1,391 @@
+import "chess.ec"
+
+struct MoveStack
+{
+   ChessMove * moves;
+   int size, count;
+};
+
+define MAXDEPTH = 4;
+// define MAXDEPTH = 6;
+define MAXDEPTH_PASS2 = 50;
+
+static MoveStack moveStack[MAXDEPTH_PASS2];
+
+void AddMoveToList(MoveStack stack, ChessState state, PieceType type, Player player, 
+                   int x1, int y1, int x2, int y2)
+{
+   if(IsMoveValid(x1,y1,x2,y2, state, null, true))
+   {
+      ChessMove * move;
+
+      if(stack.count + 1 > stack.size)
+      {
+         stack.size += 10000;
+         stack.moves = renew stack.moves ChessMove[stack.size];
+      }
+
+      move = &stack.moves[stack.count++];
+
+      move->player = player;
+      move->type = type;
+      move->x1 = x1;
+      move->y1 = y1;
+      move->x2 = x2;
+      move->y2 = y2;
+      // For now queen only...
+      move->promotion = Queen;
+   }
+}
+
+void GeneratePieceMoveList(ChessState state, int x, int y, MoveStack stack)
+{
+   Piece content = state.board[y][x];
+   Player player = content.player;
+   PieceType type = content.type;
+   int x2,y2;
+
+   switch(type)
+   {
+      case Pawn:
+      {
+         int direction = (player == White) ? 1 : -1;
+
+         AddMoveToList(stack, state, type, player, x,y,x,y+direction);
+
+         if((player == White && y == 1) ||
+            (player == Black && y == 6))
+            AddMoveToList(stack, state, type, player, x,y,x,y+direction*2);
+
+         // Capturing Move (Including en passant)
+         if(x > 0)
+            AddMoveToList(stack, state, type, player, x,y,x-1,y+direction);
+         if(x < 7)
+            AddMoveToList(stack, state, type, player, x,y,x+1,y+direction);
+         break;
+      }
+      case Knight:
+         AddMoveToList(stack, state, type, player, x,y, x+1,y+2);
+         AddMoveToList(stack, state, type, player, x,y, x+2,y+1);
+         AddMoveToList(stack, state, type, player, x,y, x+2,y-1);
+         AddMoveToList(stack, state, type, player, x,y, x+1,y-2);
+         AddMoveToList(stack, state, type, player, x,y, x-1,y-2);
+         AddMoveToList(stack, state, type, player, x,y, x-2,y-1);
+         AddMoveToList(stack, state, type, player, x,y, x-2,y+1);
+         AddMoveToList(stack, state, type, player, x,y, x-1,y+2);
+         break;
+      case Bishop:
+      {
+         for(x2 = 0; x2<8; x2++)
+         {
+            if(x2 != x)
+            {
+               y2 = y + Abs(x2 - x);
+               if(y2 < 8)
+                  AddMoveToList(stack, state, type, player, x,y,x2,y2);
+               y2 = y - Abs(x2 - x);
+               if(y2 >= 0)
+                  AddMoveToList(stack, state, type, player, x,y,x2,y2);
+            }
+         }
+         break;
+      }
+      case Rook:
+         for(x2 = 0; x2<8; x2++)
+         {
+            if(x2 != x)
+            {
+               AddMoveToList(stack, state, type, player, x,y,x2,y);
+            }
+         }
+         for(y2 = 0; y2<8; y2++)
+         {
+            if(y2 != y)
+            {
+               AddMoveToList(stack, state, type, player, x,y,x,y2);
+            }
+         }
+         break;
+      case Queen:
+         for(x2 = 0; x2<8; x2++)
+         {
+            if(x2 != x)
+            {
+               y2 = y + Abs(x2 - x);
+               if(y2 < 8)
+                  AddMoveToList(stack, state, type, player, x,y,x2,y2);
+               y2 = y - Abs(x2 - x);
+               if(y2 >= 0)
+                  AddMoveToList(stack, state, type, player, x,y,x2,y2);
+            }
+         }
+         for(x2 = 0; x2<8; x2++)
+         {
+            if(x2 != x)
+            {
+               AddMoveToList(stack, state, type, player, x,y,x2,y);
+            }
+         }
+         for(y2 = 0; y2<8; y2++)
+         {
+            if(y2 != y)
+            {
+               AddMoveToList(stack, state, type, player, x,y,x,y2);
+            }
+         }
+         break;      
+      case King:
+         AddMoveToList(stack, state, type, player, x,y, x,y+1);
+         AddMoveToList(stack, state, type, player, x,y, x+1,y+1);
+         AddMoveToList(stack, state, type, player, x,y, x+1,y);
+         AddMoveToList(stack, state, type, player, x,y, x+1,y-1);
+         AddMoveToList(stack, state, type, player, x,y, x,y-1);
+         AddMoveToList(stack, state, type, player, x,y, x-1,y-1);
+         AddMoveToList(stack, state, type, player, x,y, x-1,y);
+         AddMoveToList(stack, state, type, player, x,y, x-1,y+1);
+
+         // Castle
+         AddMoveToList(stack, state, type, player, x,y, x-2,y);
+         AddMoveToList(stack, state, type, player, x,y, x+2,y);
+         break;
+   }
+}
+
+void GenerateMoveList(ChessState state, MoveStack stack)
+{
+   int x,y;
+
+   stack.count = 0;
+   for(x = 0; x<8; x++)
+      for(y = 0; y<8; y++)
+      {
+         Piece content = state.board[y][x];
+         if(content && content.player == state.turn)
+            GeneratePieceMoveList(state, x,y, stack);
+      }
+}
+
+int EvaluateMaterial(Piece board[8][8], Player turn)
+{
+   int x,y;
+   int materialMe = 0, materialOpp = 0;
+
+   for(y = 0; y<8; y++)
+      for(x = 0; x<8; x++)
+      {
+         Piece content = board[y][x];
+         if(content != Piece {})
+         {
+            Player player = content.player;
+            PieceType piece = content.type;
+
+            if(player == turn)
+               materialMe += materialValues[piece];
+            else
+               materialOpp += materialValues[piece];
+         }
+      }
+   return materialMe - materialOpp;
+}
+
+int EvaluatePosition(ChessState state, Player turn)
+{
+   int position = 0;
+
+   // To help the mate...
+   if(state.materialValue[White] < 7)
+   {
+      if((state.kings[White].x >= 3 && state.kings[White].x <= 4) &&
+         (state.kings[White].y >= 3 && state.kings[White].y <= 4))
+      {
+         position -= 3;
+      }
+      else if((state.kings[White].x >= 2 && state.kings[White].x <= 5) &&
+              (state.kings[White].y >= 2 && state.kings[White].y <= 5))
+      {
+         position -= 2;
+      }
+      else if((state.kings[White].x >= 1 && state.kings[White].x <= 6) &&
+              (state.kings[White].y >= 1 && state.kings[White].y <= 6))
+      {
+         position -= 1;
+      }
+   }
+
+   // Castle
+   if(state.castled[White]) position -= 15;
+   if(state.castled[Black]) position += 15;
+   return position;
+}
+
+static int CompareGreater(const ChessMove a, const ChessMove b)
+{
+   if(a.rating > b.rating) return 1;
+   else if(a.rating < b.rating) return -1;
+   else
+   {
+      if(a.count < b.count) return 1;
+      else if(a.count > b.count) return -1;
+      else
+         return 0;
+   }
+}
+
+static int CompareSmaller(const ChessMove a, const ChessMove b)
+{
+   if(a.rating < b.rating) return 1;
+   else if(a.rating > b.rating) return -1;
+   else
+   {
+      if(a.count > b.count) return 1;
+      else if(a.count < b.count) return -1;
+      else
+         return 0;
+   }
+}
+
+bool FindMove(ChessState startState, int depth, int *maxDepth, ChessMove bestMove, int startRating, int * returnedRating,
+              int startCount, int * returnedCount, bool * abort)
+{
+   Player player = startState.turn;
+   MoveStack * stack = &moveStack[depth];
+   int bestRating = (player == Black) ? MININT : 200000;
+   int c;
+   int bestCount = MAXINT;
+   int maxShots = stack->count;
+
+   // Stop AI
+   if(*abort) return false;
+
+   GenerateMoveList(startState, stack);
+   if(!stack->count && !Check(startState, player, startState.kings[player].x, startState.kings[player].y))
+      bestRating = 0;
+
+   if(!stack->count)
+      bestCount = startCount;
+
+   maxShots = stack->count;
+   for(c = 0; c<maxShots; c++)
+   {
+      ChessMove * move = &stack->moves[c];
+      int newRating, newCount = MAXINT;
+      int delta = 0;
+      ChessState state = startState;
+
+      StateMakeMove(&state, move->x1, move->y1, move->x2, move->y2, move->promotion, false, &delta);
+      if(player == White)
+         delta *= -1;
+
+      if(Abs(delta) < 1000 && (depth+1 < *maxDepth))
+      {
+         FindMove(&state, depth+1, maxDepth, null, startRating + delta, &newRating, startCount+1, &newCount, abort);
+      }
+      else
+      {
+         int position;
+         position = EvaluatePosition(&state, player);
+         newRating = startRating + delta + position;
+
+         if((player == Black) ? (newRating > bestRating) : (newRating < bestRating))
+            newCount = startCount+1;
+      }
+
+      move->rating = newRating;
+      move->count = newCount;
+
+      if((player == Black) ? (newRating >= bestRating) : (newRating <= bestRating))
+      {
+         bestRating = newRating;
+         if(move->count < bestCount)
+            bestCount = move->count;
+      }
+   }
+   stack->count = maxShots;
+
+   // Pass 2
+   if(bestMove && stack->count)
+   {
+      int numGoodMoves;
+      if(player == Black)
+         qsort(stack->moves, stack->count, sizeof(ChessMove), CompareSmaller);
+      else
+         qsort(stack->moves, stack->count, sizeof(ChessMove), CompareGreater);
+
+      for(numGoodMoves = 0; numGoodMoves<stack->count; numGoodMoves++)
+      {
+         if(stack->moves[numGoodMoves].rating != bestRating)
+            break;
+         if(stack->moves[numGoodMoves].count != bestCount)
+            break;
+      }
+
+      c = GetRandom(0, numGoodMoves-1);
+      bestMove = stack->moves[c];
+   }
+   if(returnedRating)
+      *returnedRating = bestRating;
+   if(returnedCount)
+      *returnedCount = bestCount;
+   Sleep(0);
+
+   return stack->count > 0;
+}
+
+class AIThread : Thread
+{
+   Chess chess;
+   bool aiMoveResult;
+   bool abortAI;
+   ChessMove aiMove;
+
+   Timer aiTimer 
+   { 
+      this, 0.1;
+
+      bool DelayExpired()
+      {
+         if(aiMoveResult)
+         {
+            Wait();
+            chess.MakeMove(aiMove.x1, aiMove.y1, aiMove.x2, aiMove.y2, aiMove.promotion);
+            aiMoveResult = false;
+            aiTimer.Stop();
+         }
+         return true;
+      }
+   };
+
+   void Play()
+   {
+      abortAI = false;
+      app.UpdateDisplay();
+      Create();
+      aiTimer.Start();
+   }
+
+   void Abort()
+   {
+      abortAI = true;
+      Wait();
+      aiTimer.Stop();
+      aiMoveResult = false;
+   }
+
+   ChessState * chessState;
+
+   uint Main()
+   {
+      ChessState * chessState = chess.chessState;
+      int startRating = EvaluateMaterial(chessState->board, chessState->turn);
+      int depth = MAXDEPTH;
+
+      RandomSeed((int)(GetTime() * 1000));
+      aiMoveResult = FindMove(chessState, 0, &depth, &aiMove, startRating, null, 0, null, &abortAI);
+
+      return 0;
+   }
+
+public:
+   property Chess chess { set { chess = value; } }
+   property ChessState * chessState { set { chessState = value; } }
+}
diff --git a/src/chess.ec b/src/chess.ec
new file mode 100644 (file)
index 0000000..e86b27e
--- /dev/null
@@ -0,0 +1,731 @@
+/****************************************************************************
+   CHESS Game
+
+   Copyright (c) 2001 Jerome Jacovella-St-Louis
+   All Rights Reserved.
+   
+   chess.ec - Chess Main Window
+****************************************************************************/
+#ifdef ECERE_STATIC
+import static "ecere"
+#else
+import "ecere"
+#endif
+import "chess2D.ec"
+import "chess3D.ec"
+import "promotion.ec"
+import "connect.ec"
+import "about.ec"
+import "ai.ec"
+import "chessutils.ec"
+
+// --- Definitions ---
+define APPNAME = "ECERE Chess v0.4";
+define CHESS_PORT = 7778;
+define SERVER_COLOR = Black;
+define CLIENT_COLOR = White;
+
+// Network Messages
+enum ChessMessage : byte { NewGame = 1, Position = 2 };
+
+struct ChessPacket
+{
+   ChessMessage type;
+   Player player;
+   byte x1,y1,x2,y2;
+   PieceType promotion;
+};
+
+ChessApp app;
+
+class ChessApp : GuiApplication
+{
+   appName = APPNAME;
+#if defined(__WIN32__)
+   driver = "Direct3D";
+#else
+   driver = "OpenGL";
+#endif
+   Chess{};
+
+   void Main()
+   {
+      app = this;
+      GuiApplication::Main();
+   }
+}
+
+class Chess : Window
+{
+   background = gray, hasMenuBar = true, hasStatusBar = true,
+   text = APPNAME, hasClose = true, hasMaximize = true, hasMinimize = true,
+   borderStyle = sizable, hasClose = true, 
+   anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
+
+   bool hosting, local, ai;
+   Socket sockets[Player];
+
+   ChessService service { port = CHESS_PORT, chess = this };
+
+   MenuItem * driverItems;
+
+   ChessState chessState;
+   
+   StatusField stateField { statusBar, width = 200 };
+   StatusField turnField { statusBar, width = 100 };
+
+   property ChessState * chessState { get { return &chessState; } }
+
+   // AI Player Threading
+   AIThread aiThread { chess = this };
+
+   // Windows
+   
+   ListBox moveList
+   {
+      parent = this,
+      isActiveClient = true,
+      text = "MoveList",
+      autoScroll = true,
+      hasMinimize = true,
+      borderStyle = sizable,
+      hasVertScroll = true,
+      hasHorzScroll = true,
+      position = Point { x = 445 },
+      size = Size { 200, 450 }
+   };
+
+   Chess2D chess2D
+   {
+      parent = this,
+      text = "2D Chess Board",
+      chessState = &chessState
+   };
+    
+   Chess3D chess3D
+   {
+      parent = this,
+      text = "3D Chess Board",
+      anchor = Anchor { left = 0.5, top = 0.5, right = 0, bottom = 0 },
+      chessState = &chessState,
+      state = maximized
+   };
+
+   // Main Menu
+   menu = Menu {};
+
+   Menu gameMenu    { menu, "Game", g };
+   Menu networkMenu { menu, "Network", n };
+   Menu viewMenu    { menu, "View", v };
+   Menu windowMenu  { menu, "Window", w };
+   MenuDivider      { menu };
+   Menu helpMenu    { menu, "Help", h };
+
+   // Game Menu
+   MenuItem aiItem
+   {
+      gameMenu, "New AI Game\tCtrl+N", n, ctrlN;
+
+      bool NotifySelect(MenuItem selection, Modifiers mods)
+      {
+         if(EndGame())
+         {
+            ai = true;
+            chessState.gameRunning = true;
+
+            chessState.isLocalPlayer[White] = true;
+            chessState.isLocalPlayer[Black] = false;
+
+            EnableMenus();
+         
+            RandomSeed((int)(GetTime() * 10000));
+         
+            NewGame();
+         }
+         return true;
+      }
+   };
+
+   MenuItem localItem
+   {
+      gameMenu, "New Local Game\tCtrl+L", l, ctrlL;
+      bool NotifySelect(MenuItem selection, Modifiers mods)
+      {
+         if(EndGame())
+         {
+            local = true;
+            chessState.gameRunning = true;
+            chessState.isLocalPlayer[White] = true;
+            chessState.isLocalPlayer[Black] = true;
+            EnableMenus();
+            NewGame();
+         }
+         return true;
+      }
+   };
+
+   MenuItem endGameItem
+   {
+      gameMenu, "End Game", e;
+      bool NotifySelect(MenuItem selection, Modifiers mods)
+      {
+         EndGame();
+         return true;
+      }
+   };
+
+   MenuItem { gameMenu, "Exit\tAlt+F4", x, NotifySelect = MenuFileExit };
+
+   // Network Menu
+   MenuItem connectItem
+   {
+      networkMenu, "Connect...", c;
+      bool NotifySelect(MenuItem selection, Modifiers mods)
+      {
+         if(EndGame())
+         {
+            hosting = false;
+            service.Stop();
+            ConnectDialog { master = this }.Modal();
+         }
+         return true;
+      }
+   };
+
+   MenuItem disconnectItem
+   {
+      networkMenu, "Disconnect", d, NotifySelect = endGameItem.NotifySelect
+   };
+
+   MenuItem hostItem
+   {
+      networkMenu, "Host", h;
+      bool NotifySelect(MenuItem selection, Modifiers mods)
+      {
+         if(EndGame())
+         {
+            if(service.Start())
+            {
+               hosting = true;
+               EnableMenus();
+            }
+         }
+         return true;
+      }
+   };
+
+   MenuItem stopItem
+   {
+      networkMenu, "Stop Hosting", s;
+      bool NotifySelect(MenuItem selection, Modifiers mods)
+      {
+         if(EndGame())
+         {
+            hosting = false;
+            service.Stop();
+            EnableMenus();
+         }
+         return true;
+      }
+   };
+
+   // View Menu
+   MenuItem fullScreenItem
+   {
+      //viewMenu, "Full Screen", f, checkable = true;
+      bool NotifySelect(MenuItem selection, Modifiers mods)
+      {
+         app.fullScreen ^= true;
+         SetDriver();
+         anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
+         return true;
+      }
+   };
+
+   bool SetDisplayDriver(MenuItem selection, Modifiers mods)
+   {
+      //app.driver = app.drivers[selection.id];
+      SetDriver();
+      return true;
+   }
+
+   // Window Menu
+   MenuItem { windowMenu, "Next\tF6", n, NotifySelect = MenuWindowNext };
+   MenuItem { windowMenu, "Previous\tShift-F6", p, NotifySelect = MenuWindowPrevious };
+   MenuDivider { windowMenu };
+   MenuItem { windowMenu, "Windows...", w, NotifySelect = MenuWindowWindows };
+
+   //  Help Menu
+   MenuItem aboutItem
+   { 
+      helpMenu, "About...\tF1", a, f1;
+      bool NotifySelect(MenuItem selection, Modifiers mods)
+      {
+         AboutChess { master = this }.Modal();
+         return true;
+      }
+   };
+         
+   // --- Chess Utilities ---
+   bool MakeMove(int x1, int y1, int x2, int y2, PieceType promotion)
+   {
+      bool valid = false;
+      
+      PieceType type = chessState.board[y1][x1].type;
+      Player player = chessState.board[y1][x1].player;
+
+      // Pawn Promotion
+      if(type == Pawn && y2 == ((player == White) ? 7 : 0))
+      {
+         if(chessState.isLocalPlayer[chessState.turn] &&
+            IsMoveValid(x1,y1,x2,y2, chessState, null, true))
+         {
+            chess2D.Update(null);
+            chess3D.Update(null);
+            promotion = (PieceType)Promotion { master = this }.Modal();
+         }
+      }
+      
+      if(StateMakeMove(chessState, x1,y1,x2,y2, promotion, true, null))
+      {
+         valid = true;
+         
+         if(chessState.isLocalPlayer[player] && !local && !ai)
+         {
+            ChessPacket packet
+            { 
+               type = Position,
+               player = player,
+               x1 = (byte)x1,
+               y1 = (byte)y1,
+               x2 = (byte)x2,
+               y2 = (byte)y2,
+               promotion = promotion
+            };
+            sockets[player^(Player)1].Send((byte *)&packet, sizeof(ChessPacket));
+         }
+
+         if(player == Black)
+         {
+            moveList.AddStringf("               %c%c-%c%c",
+               x1+'a',y1+'1', x2+'a',y2+'1');
+         }
+         else
+         {
+            moveList.AddStringf("%3d. %c%c-%c%c",
+               chessState.numMoves/2+1, x1+'a',y1+'1', x2+'a',y2+'1');
+         }
+
+
+         // Update Status Bar
+         {
+            MoveStack stack { };
+            GenerateMoveList(chessState, stack);
+
+            delete stack.moves;
+            
+            if(Check(chessState, chessState.turn, -1, -1))
+            {
+               if(stack.count)
+               {
+                  chessState.state = Check; // TODO: Fix this
+                  stateField.text = "Check!";
+               }
+               else
+               {
+                  chessState.state = CheckMate;
+                  if(chessState.turn == White)
+                     stateField.text = "White are Checkmate.";
+                  else
+                     stateField.text = "Black are Checkmate.";
+               }
+            }
+            else if(!stack.count)
+            {
+               chessState.state = StaleMate;
+               stateField.text = "Stalemate.";
+            }
+            else
+               stateField.text = "";
+
+            // TOFIX: Gets confused with function!
+            if((chessState.state == Check || chessState.state == Normal) &&
+               chessState.isLocalPlayer[chessState.turn] && !local)
+               turnField.text = "Your turn";
+            else
+               turnField.text = "";
+         }
+      }
+   
+      chess2D.Update(null);
+      chess3D.Update(null);
+      return valid;   
+   }
+
+   void ProcessUserMove(int x1, int y1, int x2, int y2)
+   {
+      if(MakeMove(x1, y1, x2, y2, 0))
+      {
+         if(ai)
+            aiThread.Play();
+      }
+
+      chess2D.Update(null);
+
+      chess3D.Update(null);
+   }
+
+   // #define QUEENMATE
+
+   void NewGame()
+   {
+      int x,y;
+
+      moveList.Clear();
+
+      chessState.state = Normal;
+      chessState.numMoves = 0;
+
+      for(y = 0; y <8; y++)
+         for(x = 0; x<8; x++)
+            chessState.board[y][x] = Piece {};
+
+      // WHITE PIECES
+
+   #ifndef QUEENMATE
+      for(x = 0; x<8; x++)
+         chessState.board[1][x] = Piece { White, Pawn };
+      chessState.board[0][0] = chessState.board[0][7] = Piece { White, Rook };
+      chessState.board[0][1] = chessState.board[0][6] = Piece { White, Knight };
+      chessState.board[0][2] = chessState.board[0][5] = Piece { White, Bishop };
+      chessState.board[0][3] = Piece { White, Queen };
+   #endif
+      chessState.board[0][4] = Piece { White, King };
+
+      // BLACK PIECES
+   #ifndef QUEENMATE
+      for(x = 0; x<8; x++)
+         chessState.board[6][x] = Piece { Black, Pawn };
+      chessState.board[7][0] = chessState.board[7][7] = Piece { Black, Rook };
+      chessState.board[7][1] = chessState.board[7][6] = Piece { Black, Knight };
+      chessState.board[7][2] = chessState.board[7][5] = Piece { Black, Bishop };
+   #endif
+      chessState.board[7][3] = Piece { Black, Queen };
+      chessState.board[7][4] = Piece { Black, King };
+
+      // CASTLE STATUS
+   #ifndef QUEENMATE
+      chessState.kingMoved[Black] = chessState.kingMoved[White] = false;
+      chessState.kRookMoved[Black] = chessState.kRookMoved[White] = false;
+      chessState.qRookMoved[Black] = chessState.qRookMoved[White] = false;
+   #else
+      chessState.kRookMoved[Black] = chessState.kRookMoved[White] = true;
+      chessState.qRookMoved[Black] = chessState.qRookMoved[White] = true;
+   #endif
+
+      // KING POSITION
+      chessState.kings[Black].x = chessState.kings[White].x = 4;
+      chessState.kings[Black].y = 7;
+      chessState.kings[White].y = 0;
+
+   #if 0
+      chessState.kings[Black].x = 4;
+      chessState.kings[Black].y = 6;
+      chessState.kings[White].x = 4;
+      chessState.kings[White].y = 0;
+
+      chessState.kingMoved[0] = false;
+      chessState.kingMoved[1] = true;
+      chessState.kRookMoved[0] = false;
+      chessState.qRookMoved[0] = false;
+      chessState.kRookMoved[1] = true;
+      chessState.qRookMoved[1] = true;
+
+      chessState.board[0][0] = Piece { White, Rook };
+      chessState.board[0][4] = Piece { White, King };
+      chessState.board[0][7] = Piece { White, Rook };
+      chessState.board[1][0] = Piece { White, Pawn };
+      chessState.board[1][2] = Piece { White, Pawn };
+      chessState.board[1][5] = Piece { White, Pawn };
+      chessState.board[1][6] = Piece { White, Pawn };
+      chessState.board[1][7] = Piece { White, Pawn };
+      chessState.board[2][1] = Piece { White, Pawn };
+      chessState.board[2][2] = Piece { White, Knight };
+      chessState.board[4][3] = Piece { White, Pawn };
+      chessState.board[5][6] = Piece { White, Bishop };
+
+      chessState.board[5][0] = Piece { Black, Pawn };
+      chessState.board[5][1] = Piece { Black, Pawn };
+      chessState.board[5][3] = Piece { Black, Knight };
+      chessState.board[5][5] = Piece { Black, Pawn };
+      chessState.board[5][7] = Piece { Black, Bishop };
+      chessState.board[6][0] = Piece { White, Rook };
+      chessState.board[6][2] = Piece { Black, Pawn };
+      chessState.board[6][3] = Piece { Black, Pawn };
+      chessState.board[6][4] = Piece { Black, King };
+      chessState.board[7][1] = Piece { Black, Knight };
+      chessState.board[7][2] = Piece { Black, Bishop };
+   #endif
+      chessState.turn = White;
+
+      chessState.castled[White] = 
+      chessState.castled[Black] = false;
+
+      // EN PASSANT STATUS
+      chessState.enPassant.x = -1;
+      chessState.enPassant.y = -1;
+
+      chessState.pieceCount[White][Pawn]   = chessState.pieceCount[Black][Pawn] = 8;
+      chessState.pieceCount[White][Knight] = chessState.pieceCount[Black][Knight] = 2;
+      chessState.pieceCount[White][Bishop] = chessState.pieceCount[Black][Bishop] = 2;
+      chessState.pieceCount[White][Rook]   = chessState.pieceCount[Black][Rook] = 2;
+      chessState.pieceCount[White][Queen]  = chessState.pieceCount[Black][Queen] = 1;
+      chessState.pieceCount[White][King]   = chessState.pieceCount[Black][King] = 1;
+
+      chessState.materialValue[White] =
+         chessState.pieceCount[White][Pawn]   * materialValues[Pawn] +
+         chessState.pieceCount[White][Knight] * materialValues[Knight] +
+         chessState.pieceCount[White][Bishop] * materialValues[Bishop] +
+         chessState.pieceCount[White][Rook]   * materialValues[Rook] +
+         chessState.pieceCount[White][Queen]  * materialValues[Queen];
+      chessState.materialValue[Black] =
+         chessState.pieceCount[Black][Pawn]   * materialValues[Pawn] +
+         chessState.pieceCount[Black][Knight] * materialValues[Knight] +
+         chessState.pieceCount[Black][Bishop] * materialValues[Bishop] +
+         chessState.pieceCount[Black][Rook]   * materialValues[Rook] +
+         chessState.pieceCount[Black][Queen]  * materialValues[Queen];
+
+      chess2D.Update(null);
+      chess3D.Update(null);
+
+      if(chessState.isLocalPlayer[chessState.turn] && !local)
+         turnField.text = "Your turn";
+      stateField.text = "";
+
+   /*
+      // 1
+      MakeMoveChar('e',2, 'e',4);
+      MakeMoveChar('a',7, 'a',6);
+      // 2
+      MakeMoveChar('d',2, 'd',4);
+      MakeMoveChar('f',7, 'f',5);
+      // 3
+      MakeMoveChar('e',4, 'f',5);
+      MakeMoveChar('b',8, 'c',6);
+      // 4
+      MakeMoveChar('d',4, 'd',5);
+      MakeMoveChar('c',6, 'e',5);
+      // 5
+      MakeMoveChar('f',2, 'f',4);
+      MakeMoveChar('e',5, 'f',7);
+      // 6
+      MakeMoveChar('g',1, 'f',3);
+      MakeMoveChar('c',7, 'c',5);
+      // 7
+      MakeMoveChar('c',2, 'c',4);
+      MakeMoveChar('g',7, 'g',6);
+      // 8
+      MakeMoveChar('f',5, 'g',6);
+      MakeMoveChar('f',7, 'd',6);
+      // 9
+      MakeMoveChar('g',6, 'h',7);
+      MakeMoveChar('g',8, 'h',6);
+      // 10
+      MakeMoveChar('f',1, 'd',3);
+      MakeMoveChar('d',6, 'f',7);
+      // 11
+      MakeMoveChar('c',1, 'd',2);
+      MakeMoveChar('d',8, 'b',6);
+      // 12
+      MakeMoveChar('d',2, 'c',3);
+      MakeMoveChar('b',6, 'd',6);
+      // 13
+      MakeMoveChar('c',3, 'h',8);
+      MakeMoveChar('f',7, 'h',8);
+      // 14
+      MakeMoveChar('d',1, 'd',2);
+      MakeMoveChar('e',7, 'e',6);      
+   */
+   }
+
+   void MakeMoveChar(char x1, int y1, char x2, int y2)
+   {
+      MakeMove(x1 - 'a', y1 - 1, x2 - 'a', y2 - 1, Queen);
+   }
+   
+   void EnableMenus()
+   {
+      stopItem.disabled = !hosting;
+      disconnectItem.disabled = !sockets[SERVER_COLOR] && !sockets[CLIENT_COLOR];
+      endGameItem.disabled = !chessState.gameRunning;
+      hostItem.disabled = hosting;
+   }
+
+   void SetDriver()
+   {
+      int c;
+      for(c = 0; c<app.numDrivers; c++)
+         if(!strcmp(app.drivers[c], app.driver))
+         {
+            driverItems[c].checked = true;
+            break;
+         }
+   }
+
+   // --- Chess Window Class ---
+   bool OnCreate()
+   {
+      int c;
+
+      driverItems = new MenuItem[app.numDrivers];
+      for(c = 0; c<app.numDrivers; c++)
+      {
+         driverItems[c] = MenuItem { viewMenu, app.drivers[c], NotifySelect = SetDisplayDriver };
+         driverItems[c].id = c;
+         driverItems[c].isRadio = true;         
+      }
+      // this.SetPalette(palette, true);
+
+      SetDriver();
+      NewGame();
+      EnableMenus();
+
+      return true;
+   }
+
+   bool EndGame()
+   {
+      if(chessState.gameRunning && 
+         (chessState.state == Normal || chessState.state == Check))
+      {
+         if(MessageBox { type = okCancel, contents = "Quit current game?", 
+            master = this, text = "ECERE Chess" }.Modal() == cancel)
+            return false;   
+      }
+      if(sockets[SERVER_COLOR])
+         sockets[SERVER_COLOR].Disconnect(0);
+      else if(sockets[CLIENT_COLOR])
+         sockets[CLIENT_COLOR].Disconnect(0);
+      else if(local || ai)
+      {
+         if(ai) aiThread.Abort();
+         local = ai = false;
+         chessState.gameRunning = false;
+      }
+      EnableMenus();
+
+      turnField.text = "";
+      stateField.text = "";
+
+      return true;
+   }
+
+   bool OnClose(bool parentClosing)
+   {
+      return EndGame();
+   }
+
+   void OnDestroy()
+   {
+      delete sockets[Black];
+      delete sockets[White];
+      delete driverItems;
+   }
+            
+   void Connect(char * address)
+   {
+      ChessSocket socket { chess = this };
+      if(socket.Connect(address, CHESS_PORT))
+      {
+         sockets[SERVER_COLOR] = socket;
+         EnableMenus();
+
+         chessState.isLocalPlayer[CLIENT_COLOR] = true;
+         chessState.isLocalPlayer[SERVER_COLOR] = false;
+      }
+   }
+}
+
+// --- Chess Communications ---
+class ChessSocket : Socket
+{
+   Chess chess;
+   property Chess chess { set { chess = value; } }
+
+   void OnDisconnect(int code)
+   {
+      if(this == chess.sockets[CLIENT_COLOR])
+      {
+         chess.sockets[CLIENT_COLOR] = null;
+         chess.chessState->gameRunning = false;
+         chess.turnField.text = "";
+         chess.stateField.text = "";
+      }
+      else if(this == chess.sockets[SERVER_COLOR])
+      {
+         chess.sockets[SERVER_COLOR] = null;
+         chess.chessState->gameRunning = false;
+         chess.turnField.text = "";
+         chess.stateField.text = "";
+      }
+
+      chess.EnableMenus();
+      chess.chess2D.Update(null);
+      chess.chess3D.Update(null);
+   }
+   
+   uint OnReceive(const byte * buffer, uint count)
+   {
+      if(count >= sizeof(ChessPacket))
+      {
+         ChessPacket packet = *(ChessPacket *)buffer;
+         switch(packet.type)
+         {
+            case Position:
+               chess.MakeMove(packet.x1, packet.y1, packet.x2, packet.y2, packet.promotion);
+               chess.Activate();
+               break;
+            case NewGame:
+               chess.chessState->gameRunning = true;
+               chess.NewGame();
+               break;
+         }
+         return sizeof(ChessPacket);
+      }
+      return 0;
+   }
+
+   void OnConnect()
+   {
+      chess.sockets[SERVER_COLOR] = this;
+      chess.chessState->gameRunning = true;
+      chess.EnableMenus();
+      chess.NewGame();
+   }
+}
+
+class ChessService : Service
+{
+   Chess chess;
+   property Chess chess { set { chess = value; } }
+   
+   void OnAccept()
+   {
+      if(!chess.chessState->gameRunning)
+      {
+         ChessPacket packet { type = NewGame };
+
+         chess.sockets[CLIENT_COLOR] = ChessSocket { this, chess = chess };
+         chess.sockets[CLIENT_COLOR].Send((byte *)&packet, sizeof(ChessPacket));
+
+         chess.chessState->isLocalPlayer[SERVER_COLOR] = true;
+         chess.chessState->isLocalPlayer[CLIENT_COLOR] = false;
+
+         chess.NewGame();
+         chess.chessState->gameRunning = true;
+         chess.EnableMenus();
+         chess.chess2D.Update(null);
+         chess.chess3D.Update(null);
+      }
+   }
+}
diff --git a/src/chess2D.ec b/src/chess2D.ec
new file mode 100644 (file)
index 0000000..9f09b8c
--- /dev/null
@@ -0,0 +1,216 @@
+import "chess.ec"
+
+// #define FLIPBOARD
+
+/*
+define OFFSET_X = 23;
+define OFFSET_Y = 23;
+define SQUARE_W = 49;
+define SQUARE_H = 49;
+*/
+
+define OFFSET_X = 42;
+define OFFSET_Y = 42;
+define SQUARE_W = 90;
+define SQUARE_H = 90;
+
+define BOARD_WIDTH = SQUARE_W * 8;
+define BOARD_HEIGHT = SQUARE_H * 8;
+
+static char * names[12] =
+{
+   "whitePawn", "whiteKnight", "whiteBishop", "whiteRook", "whiteQueen", "whiteKing",
+   "blackPawn", "blackKnight", "blackBishop", "blackRook", "blackQueen", "blackKing"
+};
+
+class Chess2D : Window
+{
+   ChessState * chessState;
+   Point drag, moving;
+   bool dragging;
+   Bitmap piecesBMP[12];
+   //BitmapResource boardBMP { ":board.pcx", window = this };
+   BitmapResource boardBMP { ":board.jpg", window = this };
+
+   isActiveClient = true, background = black, borderStyle = fixed, hasMinimize = true;
+
+   bool OnLoadGraphics()
+   {
+      int c;
+
+#if 0
+      Bitmap chessPieces {};
+      chessPieces.Load(":pieces.pcx", null, null);
+      for(c = 0; c<12; c++)
+      {
+         piecesBMP[c] = Bitmap {};
+         piecesBMP[c].Allocate(null, SQUARE_W, SQUARE_H, 0, chessPieces.format, true);
+         piecesBMP[c].Grab(chessPieces, (c % 6) * SQUARE_H,(c / 6) * SQUARE_H);
+         piecesBMP[c].transparent = true;
+         piecesBMP[c].MakeDD(displaySystem);
+      }
+      delete chessPieces;
+#endif
+
+      for(c = 0; c<12; c++)
+      {
+         char fileName[MAX_FILENAME];
+         sprintf(fileName, ":%s.png", names[c]);
+         piecesBMP[c] = Bitmap{};
+         piecesBMP[c].LoadT(fileName, null, displaySystem);
+      }
+      return true;
+   }
+
+   bool OnResizing(int * w, int * h)
+   {
+      *w = Max(*w, BOARD_WIDTH  + OFFSET_X * 2);
+      *h = Max(*h, BOARD_HEIGHT + OFFSET_Y * 2);
+      return true;
+   }
+
+   void OnUnloadGraphics()
+   {
+      int c;
+      for(c = 0; c<12; c++)
+      {
+         piecesBMP[c].Free();
+         piecesBMP[c] = null;
+      }
+   }
+
+   void OnRedraw(Surface surface)
+   {
+      bool flip;
+      int rx, ry;
+
+   #ifdef FLIPBOARD
+      flip = chessState->turn == Black;
+   #else
+      flip = chessState->isLocalPlayer[Black] && !chessState->isLocalPlayer[White];
+   #endif
+
+      surface.SetForeground(white);
+      surface.Blit(boardBMP.bitmap, 0,0, 0,0, boardBMP.bitmap.width, boardBMP.bitmap.height);
+
+      if(chessState->gameRunning)
+      {
+         int x,y;
+         for (y=0; y<8; y++)
+         {
+                for (x=0; x<8; x++)
+                {
+               if(!dragging || x != moving.x || y != moving.y)
+               {
+                  Piece atBoard = chessState->board[y][x];
+                  int piece = (int)atBoard.player * 6 + (int)atBoard.type;
+                  if(piece)
+                  {
+                     rx = flip ? (7-x) : x;
+                     ry = flip ? y : (7-y);
+
+                     surface.Blit(piecesBMP[piece-1],
+                        OFFSET_X + rx * SQUARE_W, OFFSET_Y + ry * SQUARE_H,
+                        0,0, piecesBMP[piece-1].width, piecesBMP[piece-1].height);
+                  }
+               }
+                }
+         }
+
+         if(dragging)
+         {
+            Piece atBoard = chessState->board[moving.y][moving.x];
+            int piece = (int)atBoard.player * 6 + (int)atBoard.type;
+
+            rx = flip ? (7-drag.x) : drag.x;
+            ry = flip ? drag.y : (7-drag.y);
+
+            surface.Blit(piecesBMP[piece-1],
+               OFFSET_X + rx * SQUARE_W, OFFSET_Y + ry * SQUARE_H,
+               0,0, piecesBMP[piece-1].width, piecesBMP[piece-1].height);
+         }
+      }
+   }
+
+   bool OnLeftButtonDown(int x, int y, Modifiers mods)
+   {
+      bool flip;
+   #ifdef FLIPBOARD
+      flip = chessState->turn == Black;
+   #else
+      flip = chessState->isLocalPlayer[Black] && !chessState->isLocalPlayer[White];
+   #endif
+
+      x = (x - OFFSET_X) / SQUARE_W;
+      y = (y - OFFSET_Y) / SQUARE_H;
+
+      if(!flip) y = 7-y; else x = 7-x;
+
+      if(chessState->gameRunning &&
+         x < 8 && y < 8 && x >= 0 && y >= 0 && chessState->board[y][x] && 
+         chessState->isLocalPlayer[chessState->turn] && 
+         chessState->board[y][x].player == chessState->turn)
+      {
+         dragging = true;
+         drag.x = moving.x = x;
+         drag.y = moving.y = y;
+         Capture();
+         SetMouseRange( Box { OFFSET_X, OFFSET_Y, OFFSET_X + BOARD_WIDTH - 1, OFFSET_Y + BOARD_HEIGHT - 1 } );
+      }
+      return true;
+   }
+
+   bool OnLeftButtonUp(int x, int y, Modifiers mods)
+   {
+      bool flip;
+
+   #ifdef FLIPBOARD
+      flip = chessState->turn == Black;
+   #else
+      flip = chessState->isLocalPlayer[Black] && !chessState->isLocalPlayer[White];
+   #endif
+
+      if(chessState->gameRunning)
+      {
+         x = (x - OFFSET_X) / SQUARE_W;
+         y = (y - OFFSET_Y) / SQUARE_H;
+
+         if(!flip) y = 7-y; else x = 7-x;
+
+         if(x < 8 && y < 8 && x >= 0 && y >= 0 && dragging)
+         {
+            ReleaseCapture();
+            FreeMouseRange();
+            dragging = false;
+            ((Chess)master).ProcessUserMove(moving.x, moving.y, x, y);
+         }
+      }
+      return true;
+   }
+
+   bool OnMouseMove(int x, int y, Modifiers mods)
+   {
+      bool flip;
+
+   #ifdef FLIPBOARD
+      flip = chessState->turn == Black;
+   #else
+      flip = chessState->isLocalPlayer[Black] && !chessState->isLocalPlayer[White];
+   #endif
+
+      x = (x - OFFSET_X) / SQUARE_W;
+      y = (y - OFFSET_Y) / SQUARE_H;
+
+      if(!flip) y = 7-y; else x = 7-x;
+
+      if(x < 8 && y < 8 && x >= 0 && y >= 0 && dragging)
+      {
+         drag.x = x;    
+         drag.y = y;
+         Update(null);
+      }
+      return true;
+   }
+
+   property ChessState * chessState { set { chessState = value; } }
+}
diff --git a/src/chess3D.ec b/src/chess3D.ec
new file mode 100644 (file)
index 0000000..a09fd47
--- /dev/null
@@ -0,0 +1,421 @@
+import "chess.ec"
+
+static char * names[Player][PieceType] =
+{
+   { "", "WhitePawn", "WhiteKnigh", "WhiteBisho", "WhiteRook", "WhiteQueen", "WhiteKing" },
+   { "", "BlackPawn", "BlackKnigh", "BlackBisho", "BlackRook", "BlackQueen", "BlackKing" },
+};
+
+define SQUARE = squareSize; // 160;
+define SQUARE_OFFSET = SQUARE * 3.5f;
+
+class Chess3D : Window
+{
+   Camera camera 
+   { 
+      attached, fov = 45, zMin = 50,zMax = 5000, 
+      position = { 0, 0, -1000 },
+      orientation = Euler { 30, 30, 0 }
+   };
+
+   Object chessSet { };
+
+   Object chessBoard;
+   bool moving, lightMoving;
+   Point startPosition;
+   Euler startOrientation;
+   Light light;
+   FillModeValue fillMode;
+   ChessState * chessState;
+   double squareSize;
+   double offsetY;
+
+   bool pieceSelected;
+   Point start;
+
+   // Dragging
+   bool useDrag;
+   Point dragging;
+   double dragY;
+
+   bool antiAlias;
+
+   antiAlias = true;
+
+   menu = Menu {};
+   Menu viewMenu { menu, "View", v };
+   MenuItem itemAntiAlias
+   {
+      viewMenu, "Anti Aliased", s, checkable = true, checked = antiAlias;
+      bool NotifySelect(MenuItem selection, Modifiers mods)
+      {
+         antiAlias ^= true;
+         Update(null);
+         return true;
+      }
+   };
+
+   isActiveClient = true, background = black, borderStyle = sizable, hasMaximize = true, hasMinimize = true;
+
+   void RenderPiece(Piece atBoard, int x, int y, bool high)
+   {
+      Player player = atBoard.player;
+      PieceType type = atBoard.type;
+      
+      if(type)
+      {
+         char * name = names[player][type];
+         Object object = chessSet.Find(name);
+         if(object)
+         {
+         
+            float height = 0;
+            if(high)
+            {
+               Piece overAtBoard = chessState->board[y][x];
+               Player overPlayer = overAtBoard.player;
+               PieceType overType = overAtBoard.type;
+               if(overType)
+               {
+                  char * name = names[overPlayer][overType];
+                  Object over = chessSet.Find(name);
+                  if(over)
+                     height = over.max.y - over.min.y;
+               }
+            }
+
+            object.flags.root = true;
+
+            object.transform.position = {
+               x * SQUARE - SQUARE_OFFSET,
+               offsetY - height;
+               y * SQUARE - SQUARE_OFFSET };
+            
+            object.UpdateTransform();
+
+            object.tag = (void *)(((y)*8)+(x)+1);
+            display.DrawObject(object);
+         }
+      }
+   }
+
+   void RenderSquare(int x, int y)
+   {
+      if(x >= 0 && x < 8 && y >= 0 && y < 8)
+      {
+         Piece atBoard = chessState->board[y][x];
+
+         if(useDrag && pieceSelected)
+         {
+            if(x == start.x && y == start.y) return;
+            if(x == dragging.x && y == dragging.y)
+            {
+               Piece atBoardStart = chessState->board[start.y][start.x];
+               if(atBoard && atBoard.player != atBoardStart.player) return;
+            }
+         }
+         RenderPiece(atBoard, x, y, false);
+      }
+   }
+   
+   void RenderBoard()
+   {
+      display.DrawObject(chessBoard);
+  
+      if(chessState->gameRunning)
+      {
+         int x,y;
+         for(y=0; y<8; y++)
+                for(x=0; x<8; x++)
+               RenderSquare(x, y);
+         
+         if(useDrag && pieceSelected)
+         {
+            Piece atBoard = chessState->board[start.y][start.x];
+            Piece atBoardDrag = chessState->board[dragging.y][dragging.x];
+            RenderPiece(atBoard, dragging.x, dragging.y,
+               (dragging.x != start.x || dragging.y != start.y) &&
+               atBoardDrag && atBoardDrag.player == atBoard.player);
+         }
+      }
+   }
+
+   void OnUnloadGraphics()
+   {
+      displaySystem.ClearMaterials();
+      displaySystem.ClearTextures();
+      displaySystem.ClearMeshes();
+   }
+
+   void OnPosition(int x, int y, int w, int h)
+   {
+      camera.Setup(w, h, null);
+   }
+
+   bool OnLeftButtonDown(int x, int y, Modifiers mods)
+   {
+      OldList list {};
+                   
+      display.StartSelection(x,y, 0,0);
+      display.SetCamera(null, camera);
+      display.CollectHits();
+
+
+      if(chessState->gameRunning)
+      {
+         int cx,cy;
+         for(cy = 0; cy < 8; cy++)
+                for(cx = 0; cx < 8; cx++)
+               RenderSquare(cx, cy);
+      }
+
+      if(display.GetHits(list))
+      {
+         HitRecord hit = list.first;
+         int tag = ((int)hit.tags[0]) - 1;
+         int sx = tag & 7, sy = tag >> 3;
+
+         if(pieceSelected)
+         {
+            pieceSelected = false;
+            ((Chess)master).ProcessUserMove(start.x, start.y, sx, sy);
+         }
+         else if(chessState->board[sy][sx] && 
+            chessState->isLocalPlayer[chessState->turn] && 
+            chessState->board[sy][sx].player == chessState->turn)
+         {
+            if(useDrag)
+            {
+               Vector3D viewSpace, worldSpace;
+               display.IntersectPolygons();
+               RenderSquare(sx, sy);               
+               display.GetIntersect(viewSpace);
+
+               camera.Untransform(viewSpace, worldSpace);
+
+               dragY = worldSpace.y;
+               dragging.x = sx;
+               dragging.y = sy;
+            }
+            Capture();
+            pieceSelected = true;
+            start.x = sx;
+            start.y = sy;
+            Update(null);
+
+            OnMouseMove(x, y, mods);
+         }
+         
+         list.Free(null);
+      }
+      else if(display.DrawObject(chessBoard) && !moving && !lightMoving)
+      {
+         startPosition.x = x;
+         startPosition.y = y;
+         startOrientation = camera.orientation;
+         Capture();
+         moving = true;
+      }
+      display.SetCamera(null, null);
+      display.StopSelection();
+      Update(null);
+
+      return true;
+   }
+
+   bool OnLeftButtonUp(int x, int y, Modifiers mods)
+   {
+      if(moving)
+      {
+         ReleaseCapture();
+         moving = false;
+      }
+      if(useDrag && pieceSelected)
+      {
+         ReleaseCapture();
+         ((Chess)master).ProcessUserMove(start.x, start.y, dragging.x, dragging.y);
+         Update(null);
+         pieceSelected = false;
+      }
+      return true;
+   }
+
+   bool OnRightButtonDown(int x, int y, Modifiers mods)
+   {
+      if(!moving && !lightMoving)
+      {
+         startPosition.x = x;
+         startPosition.y = y;
+         startOrientation = light.orientation;
+         Capture();
+         lightMoving = true;
+      }
+      return true;
+   }
+
+   bool OnRightButtonUp(int x, int y, Modifiers mods)
+   {
+      if(lightMoving)
+      {
+         ReleaseCapture();
+         lightMoving = false;
+      }
+      return true;
+   }
+
+   bool OnMouseMove(int x, int y, Modifiers mods)
+   {
+      if(moving)
+      {
+         Euler angle
+         {
+            startOrientation.yaw   - (x - startPosition.x),
+            startOrientation.pitch + (y - startPosition.y),
+            0
+         };
+         if(angle.pitch > 90) angle.pitch = 90;
+         if(angle.pitch < 2)  angle.pitch = 2;
+         camera.orientation = angle;
+
+         Update(null);
+      }
+      else if(lightMoving)
+      {
+         light.orientation = Euler
+         { 
+            startOrientation.yaw + (x - startPosition.x), 
+            startOrientation.pitch + (y - startPosition.y), 
+            90 
+         };
+         
+         Update(null);
+      }
+      else if(pieceSelected && useDrag)
+      {
+         Vector3D p, v1, v2, w1, w2, vector;
+         Line line;
+         Plane plane;
+         int sx, sy;
+
+         // Compute ray of pixel
+         v1.x = 0;
+         v1.y = 0;
+         v1.z = 0;
+         p.x = (float)x;
+         p.y = (float)y;
+         p.z = 0;
+         camera.Unproject(p, v2);
+
+         // Convert ray to world space
+         camera.Untransform(v1, w1);
+         camera.Untransform(v2, w2);
+         line.p0 = w1;
+         line.delta.x = w2.x - w1.x;
+         line.delta.y = w2.y - w1.y;
+         line.delta.z = w2.z - w1.z;
+
+         // Compute plane of selection in world space
+         vector   = { 0,dragY,0 };
+         w1       = { 1,dragY,0 };
+         w2       = { 0,dragY,1 };
+         plane.FromPoints(vector, w1, w2);
+
+         // Find intersection
+         plane.IntersectLine(line, vector);
+
+         sx = (int)((vector.x + SQUARE * 4) / SQUARE);
+         sy = (int)((vector.z + SQUARE * 4) / SQUARE);
+         sx = Max(Min(sx, 7), 0);
+         sy = Max(Min(sy, 7), 0);
+
+         dragging.x = sx;
+         dragging.y = sy;
+         Update(null);
+      }
+      return true;
+   }
+
+   bool OnKeyHit(Key key, unichar ch)
+   {
+      switch((SmartKey)key)
+      {
+         case wheelDown:
+         case minus:
+            camera.position.z *= 1.1f;
+            if(camera.position.z <= -3800)
+               camera.position.z = -3800;
+            Update(null);
+            break;
+         case wheelUp:
+         case equal: camera.position.z /= 1.1f; 
+            if(camera.position.z >= -1100)
+               camera.position.z = -1100;
+            Update(null);
+            break;
+      }
+      return true;
+   }
+
+   bool OnLoadGraphics()
+   {
+      display.vSync = true;
+      chessSet.Load(":chessSet.3ds", null, displaySystem);
+
+      //chessSet.transform.orientation.RotateY(45);
+      //chessSet.UpdateTransform();
+
+      chessBoard = chessSet.Find("Rectangle0");
+      chessBoard.flags.root = true;
+
+      {
+         Object queen = chessSet.Find(names[White][Queen]);
+         Object king  = chessSet.Find(names[White][King]);
+
+         squareSize = king.transform.position.x - queen.transform.position.x;
+         offsetY = king.transform.position.y;
+      }
+
+      if(chessBoard)
+         camera.position.z = - chessBoard.radius * 2.5f;
+
+      camera.target = chessBoard;
+
+      return true;
+   }
+
+   void OnRedraw(Surface surface)
+   {
+      //surface.SetBackground(white);
+      surface.Clear(colorAndDepth);
+
+      camera.Update();
+      display.antiAlias = antiAlias;
+      display.SetCamera(surface, camera);
+      //display.fogDensity = 0.0002f;
+      display.antiAlias = antiAlias;
+
+      display.SetLight(0, light);
+      display.fillMode = fillMode;
+
+      RenderBoard();
+
+      display.SetCamera(surface, null);
+
+      display.fillMode = solid;
+   }
+
+   bool OnCreate()
+   {
+      fillMode = solid;
+      //fillMode = wireframe;
+
+      light.diffuse = white;
+      light.specular = white;
+
+      light.orientation = Euler { pitch = 90 };
+      useDrag = true;
+
+      return true;
+   }
+   property ChessState * chessState { set { chessState = value; } }
+}
diff --git a/src/chessutils.ec b/src/chessutils.ec
new file mode 100644 (file)
index 0000000..53ffb94
--- /dev/null
@@ -0,0 +1,371 @@
+/****************************************************************************
+   CHESS Game
+
+   Copyright (c) 2001 Jerome Jacovella-St-Louis
+   All Rights Reserved.
+   
+   chessutils.ec - Utilities to validate moves
+****************************************************************************/
+import "chess.ec"
+
+enum PieceType : byte { Empty, Pawn, Knight, Bishop, Rook, Queen, King };
+enum Player : byte { White, Black };
+
+int materialValues[PieceType] = { 0, 10, 30, 35, 50, 90, 10000 };
+
+class Piece : byte
+{
+public:
+   Player player : 1 : 3;
+   PieceType type : 3 : 0;
+};
+
+struct ChessMove
+{
+   int x1, y1;
+   int x2, y2;
+   Player player;
+   PieceType type;
+   int rating;
+   PieceType promotion;
+   int count;
+};
+
+enum State { Normal, Check, CheckMate, StaleMate };
+
+struct ChessState
+{
+   bool gameRunning;
+   bool isLocalPlayer[Player];
+
+   Point enPassant;
+   bool kingMoved[Player], qRookMoved[Player], kRookMoved[Player];
+   Piece board[8][8];
+   Player turn;
+   int numMoves;
+   Point kings[Player];
+   int pieceCount[Player][PieceType];
+   int materialValue[Player];
+   bool castled[Player];
+
+   State state;
+};
+
+static bool FreeWay(Piece board[8][8], int x1, int y1, int x2, int y2)
+{
+   int x,y;
+   int dx = Sgn(x2-x1), dy = Sgn(y2-y1);
+
+   for(x = x1+dx, y = y1+dy; x != x2 || y != y2; x+=dx, y+=dy)
+      if(board[y][x])
+         return false;
+   return true;
+}
+
+static bool BasicMove(Piece board[8][8], int x1, int y1, int x2, int y2)
+{
+   bool valid = false;
+   Piece source = board[y1][x1];
+   Player player = source.player;
+   Piece dest = board[y2][x2];
+
+   if(!dest || player != dest.player)
+   {
+      PieceType piece = source.type;
+      int dx = x2 - x1;
+      int dy = y2 - y1;
+
+      switch(piece)
+      {
+         case Pawn:
+         {
+            int direction = (player == White) ? 1 : -1;
+            if(dy == direction && Abs(dx) == 1 && dest)
+               valid = true;
+            break;
+         }
+         case Knight:
+            if( (Abs(dx) == 1 && Abs(dy) == 2) ||
+                (Abs(dy) == 1 && Abs(dx) == 2))
+                valid = true;
+            break;
+         case Bishop:
+            if(Abs(dx) == Abs(dy) && FreeWay(board, x1,y1,x2,y2))
+               valid = true;
+            break;
+         case Rook:
+            if((!Abs(dx) || !Abs(dy)) && FreeWay(board, x1,y1,x2,y2))
+               valid = true;
+            break;
+         case Queen:
+            if((!Abs(dx) || !Abs(dy) || Abs(dx) == Abs(dy)) && 
+               FreeWay(board, x1,y1,x2,y2))
+               valid = true;
+            break;      
+         case King:
+            if(Abs(dx) <= 1 && Abs(dy) <= 1)
+               valid = true;
+            break;
+      }
+   }
+   return valid;
+}
+
+bool Check(ChessState state, Player player, int kx, int ky)
+{
+   int x,y;
+   Piece old1 = 0xFF, old2;
+   bool checked = false;
+
+   if(kx == -1)
+   {
+      kx = state.kings[player].x;
+      ky = state.kings[player].y;
+   }
+   else
+   {
+      old1 = state.board[state.kings[player].y][state.kings[player].x];
+      state.board[state.kings[player].y][state.kings[player].x] = Piece { };
+
+      old2 = state.board[ky][kx];
+      state.board[ky][kx] = Piece { player, King };
+   }
+
+   for(y = 0; y<8; y++)
+   {
+      for(x = 0; x<8; x++)
+      {
+         Piece atBoard = state.board[y][x];
+         if(atBoard && atBoard.player != player)
+         {
+            if(BasicMove(state.board, x, y, kx, ky))
+            {
+               checked = true;
+               break;
+            }
+         }
+      }
+      if(checked)
+         break;
+   }
+
+   if(old1 != 0xFF)
+   {
+      state.board[ky][kx] = old2;
+      state.board[state.kings[player].y][state.kings[player].x] = old1;
+   }
+   return checked;
+}
+
+bool IsMoveValid(int x1, int y1, int x2, int y2, ChessState state, Piece endBoard[8][8], bool validate)
+{
+   bool valid = false;
+   Piece source = state.board[y1][x1];
+   PieceType piece = source.type;
+   Player player = source.player;
+
+   int dx = x2 - x1;
+   int dy = y2 - y1;
+
+   if(!validate || (x2 >= 0 && y2 >= 0 && x2<8 && y2<8))
+   {
+      if(!(valid = (validate ? BasicMove(state.board, x1,y1,x2,y2) : true)))
+      {
+         // Handle special moves (pawn & castle)
+         switch(piece)
+         {
+            case Pawn:
+            {
+               int direction = (player == White) ? 1 : -1;
+               int start = (player == White) ? 1 : 6;
+               if(dy == direction)
+               {
+                  // Normal Move
+                  if(x1 == x2 && !state.board[y2][x2])
+                     valid = true;
+                  // En Passant
+                  else if(Abs(dx) == 1 && 
+                          x2 == state.enPassant.x && y2 == state.enPassant.y + direction)
+                     valid = true;
+               }
+               // First 2 Squares Move
+               else if(y2 - y1 == direction * 2 && y1 == start && x1 == x2 
+                  && !state.board[y1+direction][x1] && !state.board[y2][x1])
+                  valid = true;
+               break;
+            }
+            case King:
+               // Castle
+               if(!dy && Abs(dx) == 2)
+               {
+                  if(!state.kingMoved[player] && !Check(state, player, x1, y1))
+                  {
+                     // King Side
+                     if(dx == 2 && !state.kRookMoved[player] && 
+                        !state.board[y1][5] && !state.board[y1][6] &&
+                        !Check(state, player, 5, y1))
+                        valid = true;
+                     // Queen Side
+                     else if(dx == -2 && !state.qRookMoved[player] &&
+                        !state.board[y1][3] && !state.board[y1][2] && !state.board[y1][1] &&
+                        !Check(state, player, 3, y1))
+                        valid = true;
+                  }
+               }
+               break;
+         }
+      }
+   }
+
+   if(valid)
+   {
+      if(validate || endBoard)
+      {
+         ChessState tmpState = state;
+         tmpState = state;
+         tmpState.board[y1][x1] = Piece {};
+
+         // Handle special moves (pawn & castle)
+         switch(piece)
+         {
+            case Pawn:
+            {
+               int direction = (player == White) ? 1 : -1;
+               int start = (player == White) ? 1 : 6;
+               if(dy == direction)
+               {
+                  // Normal Move
+                  if(Abs(dx) == 1 && x2 == state.enPassant.x && y2 == state.enPassant.y + direction)
+                     tmpState.board[state.enPassant.y][state.enPassant.x] = Piece {};
+               }
+               break;
+            }
+            case King:
+               // *** Castle ***
+
+               // King Side
+               if(dx == 2)
+               {
+                  tmpState.board[y1][7] = Piece { };
+                  tmpState.board[y1][5] = Piece { player, Rook };
+               }
+               // Queen Side
+               else if(dx == -2)
+               {
+                  tmpState.board[y1][0] = Piece { };
+                  tmpState.board[y1][3] = Piece { player, Rook };
+               }
+
+               // Keep Track of King
+               tmpState.kings[player].x = x2;
+               tmpState.kings[player].y = y2;
+               break;
+         }
+
+         tmpState.board[y2][x2] = state.board[y1][x1];
+
+         valid = validate ? !Check(tmpState, player, -1, -1) : true;
+         if(valid && endBoard)
+            CopyBytes(endBoard, tmpState.board, 64);
+      }
+   }
+   return valid;
+}
+
+bool StateMakeMove(ChessState state, int x1, int y1, int x2, int y2, PieceType promotion, bool validate, int * delta)
+{
+   bool valid = false;
+   PieceType type = state.board[y1][x1].type;
+   Player player = state.board[y1][x1].player;
+
+   PieceType captured = state.board[y2][x2].type;
+   if(captured)
+   {
+      if(x2 == 7 && y2 == ((player == White) ? 7 : 0) && !state.kRookMoved[(Player)!player])
+         state.kRookMoved[(Player)!player] = true;
+      else if(x2 == 0 && y2 == ((player == White) ? 7 : 0) && !state.qRookMoved[(Player)!player])
+         state.qRookMoved[(Player)!player] = true;
+
+      state.pieceCount[(Player)!player][captured]--;
+      state.materialValue[(Player)!player] -= materialValues[captured];
+      if(delta)
+         *delta = materialValues[captured];
+   }
+   // En Passant
+   else if(type == Pawn && Abs(x2-x1) == 1)
+   {
+      state.pieceCount[(Player)!player][Pawn]--;
+      state.materialValue[(Player)!player] -= materialValues[Pawn];
+      if(delta)
+         *delta = materialValues[Pawn];
+   }
+   
+   if(IsMoveValid(x1,y1,x2,y2, state, state.board, validate))
+   {
+      valid = true;
+
+      // En Passant
+      if(type == Pawn && Abs(y2-y1) == 2)
+      {
+         state.enPassant.x = x2;
+         state.enPassant.y = y2;
+      }
+      else
+      {
+         state.enPassant.x = -1;
+         state.enPassant.y = -1;
+      }
+
+      // Castle
+      if(type == King)
+      {
+         switch(x2-x1)
+         {
+            // King Side Castle
+            case 2:
+               state.kRookMoved[player] = true;
+               state.castled[player] = true;
+               break;
+            // Queen Side Castle
+            case -2:
+               state.qRookMoved[player] = true;
+               state.castled[player] = true;
+               break;
+         }
+      }
+
+      // Rook moved (can't castle with it)
+      if(type == Rook && y1 == ((player == White) ? 0 : 7))
+      {
+         if(!state.qRookMoved[player] && x1 == 0)
+            state.qRookMoved[player] = true;
+         else if(!state.kRookMoved[player] && x1 == 7)
+            state.kRookMoved[player] = true;
+      }
+
+      // Pawn Promotion
+      if(type == Pawn && y2 == ((player == White) ? 7 : 0))
+      {
+         state.board[y2][x2] = Piece { player, promotion };
+
+         state.pieceCount[player][Pawn]--;
+         state.pieceCount[player][promotion]++;
+         state.materialValue[player] += materialValues[promotion] - materialValues[Pawn];
+         if(delta)
+            *delta += materialValues[promotion] - materialValues[Pawn];
+      }
+
+      // Keep track of the kings
+      if(type == King)
+      {
+         state.kings[player].x = x2;
+         state.kings[player].y = y2;
+
+         state.kingMoved[player] = true;
+      }
+
+      state.turn ^= 1;
+      state.numMoves ++;
+   }
+   return valid;   
+}
diff --git a/src/connect.ec b/src/connect.ec
new file mode 100644 (file)
index 0000000..d37095e
--- /dev/null
@@ -0,0 +1,38 @@
+import "chess.ec"
+
+class ConnectDialog : Window
+{
+   minClientSize = Size { 300, 100 };
+   tabCycle = true, background = activeBorder, hasClose = true, text = "Connect to server";
+
+   Button ok
+   {
+      parent = this, bevel = true, isDefault = true, text = "OK",
+      size = Size { w = 80 }, anchor = Anchor { horz = -48, bottom = 10 };
+
+      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
+      {
+         ((Chess)master).Connect(address.line.text);
+         Destroy(0);
+         return true;
+      }
+   };
+
+   Button cancel
+   {
+      parent = this, bevel = true, text = "Cancel", size = Size { w = 80 }, hotKey = escape;
+      anchor = Anchor { horz = 48, bottom = 10 };
+
+      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
+      {
+         Destroy(0);
+         return false;
+      }
+   };
+
+   EditBox address
+   {
+      parent = this, textHorzScroll = true,  size = Size { w = 200 }, anchor = Anchor { top = 10 },
+      line.text = "localhost"
+   };
+}
diff --git a/src/promotion.ec b/src/promotion.ec
new file mode 100644 (file)
index 0000000..cbb2a19
--- /dev/null
@@ -0,0 +1,43 @@
+/****************************************************************************
+   CHESS Game
+
+   Copyright (c) 2001 Jerome Jacovella-St-Louis
+   All Rights Reserved.
+   
+   promotion.c - Pawn Promotion Window
+****************************************************************************/
+import "chessutils.ec"
+
+class Promotion : Window
+{
+   background = gray, text = "Pawn Promotion", borderStyle = fixed, tabCycle = true,
+   minClientSize = Size { 120, 140 };
+
+   bool ButtonClicked(Button button, int x, int y, Modifiers mods)
+   {
+      Destroy(button.id);
+      return true;
+   }
+
+   Button
+   {
+      parent = this, bevel = true, text = "Knight", position = Point { 20, 10 },
+      size = Size { 80, 20 }, id = PieceType::Knight, NotifyClicked = ButtonClicked
+   };
+   Button
+   {
+      parent = this, bevel = true, text = "Bishop", position = Point { 20, 35 },
+      size = Size { 80, 20 }, id = PieceType::Bishop, NotifyClicked = ButtonClicked
+   };
+   Button
+   {
+      parent = this, bevel = true, text = "Rook", position = Point { 20, 60 },
+      size = Size { 80, 20 }, id = PieceType::Rook, NotifyClicked = ButtonClicked
+   };
+   Button
+   {
+      parent = this, bevel = true, text = "Queen", position = Point { 20, 85 },
+      size = Size { 80, 20 }, id = PieceType::Queen, NotifyClicked = ButtonClicked,
+      isDefault = true
+   };
+}