--- /dev/null
+ 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.
--- /dev/null
+{
+ "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
--- /dev/null
+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");
+ }
+}
--- /dev/null
+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; } }
+}
--- /dev/null
+/****************************************************************************
+ 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);
+ }
+ }
+}
--- /dev/null
+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; } }
+}
--- /dev/null
+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; } }
+}
--- /dev/null
+/****************************************************************************
+ 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;
+}
--- /dev/null
+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"
+ };
+}
--- /dev/null
+/****************************************************************************
+ 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
+ };
+}