1 /****************************************************************************
4 Copyright (c) 2001 Jerome Jacovella-St-Louis
7 chess.ec - Chess Main Window
8 ****************************************************************************/
20 import "chessutils.ec"
22 // --- Definitions ---
23 define APPNAME = "ECERE Chess v0.4";
24 define CHESS_PORT = 7778;
25 define SERVER_COLOR = Black;
26 define CLIENT_COLOR = White;
29 enum ChessMessage : byte { NewGame = 1, Position = 2 };
41 class ChessApp : GuiApplication
44 /*#if defined(__WIN32__)
54 GuiApplication::Main();
58 enum GameAction { newAIGame, newLocalGame, endGame, close, connect, host, stop };
62 define stateWidth = 300;
63 define turnWidth = 150;
65 define stateWidth = 200;
66 define turnWidth = 100;
71 background = gray, hasMenuBar = true, hasStatusBar = true,
74 #if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
75 hasClose = true, hasMaximize = true, hasMinimize = true,
76 borderStyle = sizable,
78 anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
80 bool hosting, local, ai;
82 Socket sockets[Player];
84 ChessService service { port = CHESS_PORT, chess = this };
87 MenuItem * driverItems;
89 ChessState chessState;
91 StatusField stateField { statusBar, width = stateWidth};
92 StatusField turnField { statusBar, width = turnWidth };
94 property ChessState * chessState { get { return &chessState; } }
96 // AI Player Threading
97 AIThread aiThread { chess = this };
104 isActiveClient = true,
108 borderStyle = sizable,
109 hasVertScroll = true,
110 hasHorzScroll = true,
111 position = Point { x = 445 },
112 size = Size { 200, 450 }
118 text = "2D Chess Board",
119 chessState = &chessState
125 text = "3D Chess Board",
126 anchor = Anchor { left = 0.5, top = 0.5, right = 0, bottom = 0 },
127 chessState = &chessState,
134 Menu gameMenu { menu, "Game", g };
135 Menu networkMenu { menu, "Network", n };
136 Menu viewMenu { menu, "View", v };
137 Menu windowMenu { menu, "Window", w };
138 MenuDivider { menu };
139 Menu helpMenu { menu, "Help", h };
144 gameMenu, "New AI Game\tCtrl+N", n, ctrlN;
146 bool NotifySelect(MenuItem selection, Modifiers mods)
148 if(EndGame(newAIGame))
151 chessState.gameRunning = true;
153 chessState.isLocalPlayer[White] = true;
154 chessState.isLocalPlayer[Black] = false;
158 RandomSeed((uint)(((uint64)(GetTime() * 1000)) & MAXDWORD));
168 gameMenu, "New Local Game\tCtrl+L", l, ctrlL;
169 bool NotifySelect(MenuItem selection, Modifiers mods)
171 if(EndGame(newLocalGame))
174 chessState.gameRunning = true;
175 chessState.isLocalPlayer[White] = true;
176 chessState.isLocalPlayer[Black] = true;
186 gameMenu, "End Game", e;
187 bool NotifySelect(MenuItem selection, Modifiers mods)
194 MenuItem { gameMenu, "Exit\tAlt+F4", x, NotifySelect = MenuFileExit };
200 networkMenu, "Connect...", c;
201 bool NotifySelect(MenuItem selection, Modifiers mods)
207 ConnectDialog { master = this, isModal = true }.Create();
213 MenuItem disconnectItem
215 networkMenu, "Disconnect", d, NotifySelect = endGameItem.NotifySelect
220 networkMenu, "Host", h;
221 bool NotifySelect(MenuItem selection, Modifiers mods)
237 networkMenu, "Stop Hosting", s;
238 bool NotifySelect(MenuItem selection, Modifiers mods)
252 MenuItem fullScreenItem
254 //viewMenu, "Full Screen", f, checkable = true;
255 bool NotifySelect(MenuItem selection, Modifiers mods)
257 app.fullScreen ^= true;
259 anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
264 bool SetDisplayDriver(MenuItem selection, Modifiers mods)
266 //app.driver = app.drivers[selection.id];
272 MenuItem { windowMenu, "Next\tF6", n, NotifySelect = MenuWindowNext };
273 MenuItem { windowMenu, "Previous\tShift-F6", p, NotifySelect = MenuWindowPrevious };
274 MenuDivider { windowMenu };
275 MenuItem { windowMenu, "Windows...", w, NotifySelect = MenuWindowWindows };
280 helpMenu, "About...\tF1", a, f1;
281 bool NotifySelect(MenuItem selection, Modifiers mods)
283 AboutChess { master = this, isModal = true }.Create();
288 int cx1, cy1, cx2, cy2;
290 bool DoMove(int x1, int y1, int x2, int y2, PieceType promotion)
293 PieceType type = chessState.board[y1][x1].type;
294 Player player = chessState.board[y1][x1].player;
296 if(StateMakeMove(chessState, x1,y1,x2,y2, promotion, true, null))
300 if(chessState.isLocalPlayer[player] && !local && !ai)
310 promotion = promotion
313 sockets[player^(Player)1].Send((byte *)&packet, sizeof(ChessPacket));
319 moveList.AddStringf(" %c%c-%c%c",
320 x1+'a',y1+'1', x2+'a',y2+'1');
324 moveList.AddStringf("%3d. %c%c-%c%c",
325 chessState.numMoves/2+1, x1+'a',y1+'1', x2+'a',y2+'1');
332 GenerateMoveList(chessState, stack);
336 if(Check(chessState, chessState.turn, -1, -1))
340 chessState.state = Check; // TODO: Fix this
341 stateField.text = "Check!";
345 chessState.state = CheckMate;
346 if(chessState.turn == White)
347 stateField.text = "White are Checkmate.";
349 stateField.text = "Black are Checkmate.";
352 else if(!stack.count)
354 chessState.state = StaleMate;
355 stateField.text = "Stalemate.";
358 stateField.text = "";
360 // TOFIX: Gets confused with function!
361 if((chessState.state == Check || chessState.state == Normal) &&
362 chessState.isLocalPlayer[chessState.turn] && !local)
363 turnField.text = "Your turn";
369 chess2D.Update(null);
370 chess3D.Update(null);
374 // --- Chess Utilities ---
375 bool MakeMove(int x1, int y1, int x2, int y2, PieceType promotion)
379 PieceType type = chessState.board[y1][x1].type;
380 Player player = chessState.board[y1][x1].player;
383 if(type == Pawn && y2 == ((player == White) ? 7 : 0))
386 if(chessState.isLocalPlayer[chessState.turn] &&
387 (valid = IsMoveValid(x1,y1,x2,y2, chessState, null, true)))
390 cx1 = x1, cy1 = y1, cx2 = x2, cy2 = y2;
391 chess2D.Update(null);
392 chess3D.Update(null);
393 promotion = (PieceType)Promotion
398 void Chess::NotifyDestroyed(Window promotionDlg, DialogResult r)
400 DoMove(cx1, cy1, cx2, cy2, (PieceType)r);
408 valid = DoMove(x1, y1, x2, y2, promotion);
412 void ProcessUserMove(int x1, int y1, int x2, int y2)
414 if(MakeMove(x1, y1, x2, y2, 0))
420 chess2D.Update(null);
422 chess3D.Update(null);
433 chessState.state = Normal;
434 chessState.numMoves = 0;
436 for(y = 0; y <8; y++)
438 chessState.board[y][x] = Piece {};
444 chessState.board[1][x] = Piece { White, Pawn };
445 chessState.board[0][0] = chessState.board[0][7] = Piece { White, Rook };
446 chessState.board[0][1] = chessState.board[0][6] = Piece { White, Knight };
447 chessState.board[0][2] = chessState.board[0][5] = Piece { White, Bishop };
448 chessState.board[0][3] = Piece { White, Queen };
450 chessState.board[0][4] = Piece { White, King };
455 chessState.board[6][x] = Piece { Black, Pawn };
456 chessState.board[7][0] = chessState.board[7][7] = Piece { Black, Rook };
457 chessState.board[7][1] = chessState.board[7][6] = Piece { Black, Knight };
458 chessState.board[7][2] = chessState.board[7][5] = Piece { Black, Bishop };
460 chessState.board[7][3] = Piece { Black, Queen };
461 chessState.board[7][4] = Piece { Black, King };
465 chessState.kingMoved[Black] = chessState.kingMoved[White] = false;
466 chessState.kRookMoved[Black] = chessState.kRookMoved[White] = false;
467 chessState.qRookMoved[Black] = chessState.qRookMoved[White] = false;
469 chessState.kRookMoved[Black] = chessState.kRookMoved[White] = true;
470 chessState.qRookMoved[Black] = chessState.qRookMoved[White] = true;
474 chessState.kings[Black].x = chessState.kings[White].x = 4;
475 chessState.kings[Black].y = 7;
476 chessState.kings[White].y = 0;
479 chessState.kings[Black].x = 4;
480 chessState.kings[Black].y = 6;
481 chessState.kings[White].x = 4;
482 chessState.kings[White].y = 0;
484 chessState.kingMoved[0] = false;
485 chessState.kingMoved[1] = true;
486 chessState.kRookMoved[0] = false;
487 chessState.qRookMoved[0] = false;
488 chessState.kRookMoved[1] = true;
489 chessState.qRookMoved[1] = true;
491 chessState.board[0][0] = Piece { White, Rook };
492 chessState.board[0][4] = Piece { White, King };
493 chessState.board[0][7] = Piece { White, Rook };
494 chessState.board[1][0] = Piece { White, Pawn };
495 chessState.board[1][2] = Piece { White, Pawn };
496 chessState.board[1][5] = Piece { White, Pawn };
497 chessState.board[1][6] = Piece { White, Pawn };
498 chessState.board[1][7] = Piece { White, Pawn };
499 chessState.board[2][1] = Piece { White, Pawn };
500 chessState.board[2][2] = Piece { White, Knight };
501 chessState.board[4][3] = Piece { White, Pawn };
502 chessState.board[5][6] = Piece { White, Bishop };
504 chessState.board[5][0] = Piece { Black, Pawn };
505 chessState.board[5][1] = Piece { Black, Pawn };
506 chessState.board[5][3] = Piece { Black, Knight };
507 chessState.board[5][5] = Piece { Black, Pawn };
508 chessState.board[5][7] = Piece { Black, Bishop };
509 chessState.board[6][0] = Piece { White, Rook };
510 chessState.board[6][2] = Piece { Black, Pawn };
511 chessState.board[6][3] = Piece { Black, Pawn };
512 chessState.board[6][4] = Piece { Black, King };
513 chessState.board[7][1] = Piece { Black, Knight };
514 chessState.board[7][2] = Piece { Black, Bishop };
516 chessState.turn = White;
518 chessState.castled[White] =
519 chessState.castled[Black] = false;
522 chessState.enPassant.x = -1;
523 chessState.enPassant.y = -1;
525 chessState.pieceCount[White][Pawn] = chessState.pieceCount[Black][Pawn] = 8;
526 chessState.pieceCount[White][Knight] = chessState.pieceCount[Black][Knight] = 2;
527 chessState.pieceCount[White][Bishop] = chessState.pieceCount[Black][Bishop] = 2;
528 chessState.pieceCount[White][Rook] = chessState.pieceCount[Black][Rook] = 2;
529 chessState.pieceCount[White][Queen] = chessState.pieceCount[Black][Queen] = 1;
530 chessState.pieceCount[White][King] = chessState.pieceCount[Black][King] = 1;
532 chessState.materialValue[White] =
533 chessState.pieceCount[White][Pawn] * materialValues[Pawn] +
534 chessState.pieceCount[White][Knight] * materialValues[Knight] +
535 chessState.pieceCount[White][Bishop] * materialValues[Bishop] +
536 chessState.pieceCount[White][Rook] * materialValues[Rook] +
537 chessState.pieceCount[White][Queen] * materialValues[Queen];
538 chessState.materialValue[Black] =
539 chessState.pieceCount[Black][Pawn] * materialValues[Pawn] +
540 chessState.pieceCount[Black][Knight] * materialValues[Knight] +
541 chessState.pieceCount[Black][Bishop] * materialValues[Bishop] +
542 chessState.pieceCount[Black][Rook] * materialValues[Rook] +
543 chessState.pieceCount[Black][Queen] * materialValues[Queen];
545 chess2D.Update(null);
546 chess3D.Update(null);
548 if(chessState.isLocalPlayer[chessState.turn] && !local)
549 turnField.text = "Your turn";
550 stateField.text = "";
554 MakeMoveChar('e',2, 'e',4);
555 MakeMoveChar('a',7, 'a',6);
557 MakeMoveChar('d',2, 'd',4);
558 MakeMoveChar('f',7, 'f',5);
560 MakeMoveChar('e',4, 'f',5);
561 MakeMoveChar('b',8, 'c',6);
563 MakeMoveChar('d',4, 'd',5);
564 MakeMoveChar('c',6, 'e',5);
566 MakeMoveChar('f',2, 'f',4);
567 MakeMoveChar('e',5, 'f',7);
569 MakeMoveChar('g',1, 'f',3);
570 MakeMoveChar('c',7, 'c',5);
572 MakeMoveChar('c',2, 'c',4);
573 MakeMoveChar('g',7, 'g',6);
575 MakeMoveChar('f',5, 'g',6);
576 MakeMoveChar('f',7, 'd',6);
578 MakeMoveChar('g',6, 'h',7);
579 MakeMoveChar('g',8, 'h',6);
581 MakeMoveChar('f',1, 'd',3);
582 MakeMoveChar('d',6, 'f',7);
584 MakeMoveChar('c',1, 'd',2);
585 MakeMoveChar('d',8, 'b',6);
587 MakeMoveChar('d',2, 'c',3);
588 MakeMoveChar('b',6, 'd',6);
590 MakeMoveChar('c',3, 'h',8);
591 MakeMoveChar('f',7, 'h',8);
593 MakeMoveChar('d',1, 'd',2);
594 MakeMoveChar('e',7, 'e',6);
598 void MakeMoveChar(char x1, int y1, char x2, int y2)
600 MakeMove(x1 - 'a', y1 - 1, x2 - 'a', y2 - 1, Queen);
606 stopItem.disabled = !hosting;
607 disconnectItem.disabled = !sockets[SERVER_COLOR] && !sockets[CLIENT_COLOR];
608 hostItem.disabled = hosting;
610 endGameItem.disabled = !chessState.gameRunning;
616 for(c = 0; c<app.numDrivers; c++)
617 if(!strcmp(app.drivers[c], app.driver))
619 driverItems[c].checked = true;
624 // --- Chess Window Class ---
629 driverItems = new MenuItem[app.numDrivers];
630 for(c = 0; c<app.numDrivers; c++)
632 driverItems[c] = MenuItem { viewMenu, app.drivers[c], NotifySelect = SetDisplayDriver };
633 driverItems[c].id = c;
634 driverItems[c].isRadio = true;
636 // this.SetPalette(palette, true);
645 void DoEndGame(GameAction action)
648 if(sockets[SERVER_COLOR])
649 sockets[SERVER_COLOR].Disconnect(0);
650 else if(sockets[CLIENT_COLOR])
651 sockets[CLIENT_COLOR].Disconnect(0);
656 if(ai) aiThread.Abort();
658 chessState.gameRunning = false;
663 stateField.text = "";
667 case newAIGame: aiItem.NotifySelect(this, aiItem, 0); break;
668 case newLocalGame: localItem.NotifySelect(this, localItem, 0); break;
672 GameAction nextAction;
674 bool EndGame(GameAction action)
676 if(chessState.gameRunning &&
677 (chessState.state == Normal || chessState.state == Check))
682 type = okCancel, contents = "Quit current game?",
683 master = this, text = "ECERE Chess";
686 void Chess::NotifyDestroyed(Window msgBox, DialogResult result)
689 DoEndGame(nextAction);
697 bool OnClose(bool parentClosing)
699 return EndGame(close);
705 delete sockets[Black];
706 delete sockets[White];
712 void Connect(const char * address)
714 ChessSocket socket { chess = this };
715 if(socket.Connect(address, CHESS_PORT))
717 sockets[SERVER_COLOR] = socket;
720 chessState.isLocalPlayer[CLIENT_COLOR] = true;
721 chessState.isLocalPlayer[SERVER_COLOR] = false;
727 // --- Chess Communications ---
729 class ChessSocket : Socket
732 property Chess chess { set { chess = value; } }
734 void OnDisconnect(int code)
736 if(this == chess.sockets[CLIENT_COLOR])
738 chess.sockets[CLIENT_COLOR] = null;
739 chess.chessState->gameRunning = false;
740 chess.turnField.text = "";
741 chess.stateField.text = "";
743 else if(this == chess.sockets[SERVER_COLOR])
745 chess.sockets[SERVER_COLOR] = null;
746 chess.chessState->gameRunning = false;
747 chess.turnField.text = "";
748 chess.stateField.text = "";
752 chess.chess2D.Update(null);
753 chess.chess3D.Update(null);
756 uint OnReceive(const byte * buffer, uint count)
758 if(count >= sizeof(ChessPacket))
760 ChessPacket packet = *(ChessPacket *)buffer;
764 chess.MakeMove(packet.x1, packet.y1, packet.x2, packet.y2, packet.promotion);
768 chess.chessState->gameRunning = true;
772 return sizeof(ChessPacket);
779 chess.sockets[SERVER_COLOR] = this;
780 chess.chessState->gameRunning = true;
786 class ChessService : Service
789 property Chess chess { set { chess = value; } }
793 if(!chess.chessState->gameRunning)
795 ChessPacket packet { type = NewGame };
797 chess.sockets[CLIENT_COLOR] = ChessSocket { this, chess = chess };
798 chess.sockets[CLIENT_COLOR].Send((byte *)&packet, sizeof(ChessPacket));
800 chess.chessState->isLocalPlayer[SERVER_COLOR] = true;
801 chess.chessState->isLocalPlayer[CLIENT_COLOR] = false;
804 chess.chessState->gameRunning = true;
806 chess.chess2D.Update(null);
807 chess.chess3D.Update(null);