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)
377 bool makeMove = true;
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))
385 if(chessState.isLocalPlayer[chessState.turn] &&
386 IsMoveValid(x1,y1,x2,y2, chessState, null, true))
389 cx1 = x1, cy1 = y1, cx2 = x2, cy2 = y2;
390 chess2D.Update(null);
391 chess3D.Update(null);
392 promotion = (PieceType)Promotion
397 void Chess::NotifyDestroyed(Window promotionDlg, DialogResult r)
399 DoMove(cx1, cy1, cx2, cy2, (PieceType)r);
406 return makeMove ? DoMove(x1, y1, x2, y2, promotion) : false;
409 void ProcessUserMove(int x1, int y1, int x2, int y2)
411 if(MakeMove(x1, y1, x2, y2, 0))
417 chess2D.Update(null);
419 chess3D.Update(null);
430 chessState.state = Normal;
431 chessState.numMoves = 0;
433 for(y = 0; y <8; y++)
435 chessState.board[y][x] = Piece {};
441 chessState.board[1][x] = Piece { White, Pawn };
442 chessState.board[0][0] = chessState.board[0][7] = Piece { White, Rook };
443 chessState.board[0][1] = chessState.board[0][6] = Piece { White, Knight };
444 chessState.board[0][2] = chessState.board[0][5] = Piece { White, Bishop };
445 chessState.board[0][3] = Piece { White, Queen };
447 chessState.board[0][4] = Piece { White, King };
452 chessState.board[6][x] = Piece { Black, Pawn };
453 chessState.board[7][0] = chessState.board[7][7] = Piece { Black, Rook };
454 chessState.board[7][1] = chessState.board[7][6] = Piece { Black, Knight };
455 chessState.board[7][2] = chessState.board[7][5] = Piece { Black, Bishop };
457 chessState.board[7][3] = Piece { Black, Queen };
458 chessState.board[7][4] = Piece { Black, King };
462 chessState.kingMoved[Black] = chessState.kingMoved[White] = false;
463 chessState.kRookMoved[Black] = chessState.kRookMoved[White] = false;
464 chessState.qRookMoved[Black] = chessState.qRookMoved[White] = false;
466 chessState.kRookMoved[Black] = chessState.kRookMoved[White] = true;
467 chessState.qRookMoved[Black] = chessState.qRookMoved[White] = true;
471 chessState.kings[Black].x = chessState.kings[White].x = 4;
472 chessState.kings[Black].y = 7;
473 chessState.kings[White].y = 0;
476 chessState.kings[Black].x = 4;
477 chessState.kings[Black].y = 6;
478 chessState.kings[White].x = 4;
479 chessState.kings[White].y = 0;
481 chessState.kingMoved[0] = false;
482 chessState.kingMoved[1] = true;
483 chessState.kRookMoved[0] = false;
484 chessState.qRookMoved[0] = false;
485 chessState.kRookMoved[1] = true;
486 chessState.qRookMoved[1] = true;
488 chessState.board[0][0] = Piece { White, Rook };
489 chessState.board[0][4] = Piece { White, King };
490 chessState.board[0][7] = Piece { White, Rook };
491 chessState.board[1][0] = Piece { White, Pawn };
492 chessState.board[1][2] = Piece { White, Pawn };
493 chessState.board[1][5] = Piece { White, Pawn };
494 chessState.board[1][6] = Piece { White, Pawn };
495 chessState.board[1][7] = Piece { White, Pawn };
496 chessState.board[2][1] = Piece { White, Pawn };
497 chessState.board[2][2] = Piece { White, Knight };
498 chessState.board[4][3] = Piece { White, Pawn };
499 chessState.board[5][6] = Piece { White, Bishop };
501 chessState.board[5][0] = Piece { Black, Pawn };
502 chessState.board[5][1] = Piece { Black, Pawn };
503 chessState.board[5][3] = Piece { Black, Knight };
504 chessState.board[5][5] = Piece { Black, Pawn };
505 chessState.board[5][7] = Piece { Black, Bishop };
506 chessState.board[6][0] = Piece { White, Rook };
507 chessState.board[6][2] = Piece { Black, Pawn };
508 chessState.board[6][3] = Piece { Black, Pawn };
509 chessState.board[6][4] = Piece { Black, King };
510 chessState.board[7][1] = Piece { Black, Knight };
511 chessState.board[7][2] = Piece { Black, Bishop };
513 chessState.turn = White;
515 chessState.castled[White] =
516 chessState.castled[Black] = false;
519 chessState.enPassant.x = -1;
520 chessState.enPassant.y = -1;
522 chessState.pieceCount[White][Pawn] = chessState.pieceCount[Black][Pawn] = 8;
523 chessState.pieceCount[White][Knight] = chessState.pieceCount[Black][Knight] = 2;
524 chessState.pieceCount[White][Bishop] = chessState.pieceCount[Black][Bishop] = 2;
525 chessState.pieceCount[White][Rook] = chessState.pieceCount[Black][Rook] = 2;
526 chessState.pieceCount[White][Queen] = chessState.pieceCount[Black][Queen] = 1;
527 chessState.pieceCount[White][King] = chessState.pieceCount[Black][King] = 1;
529 chessState.materialValue[White] =
530 chessState.pieceCount[White][Pawn] * materialValues[Pawn] +
531 chessState.pieceCount[White][Knight] * materialValues[Knight] +
532 chessState.pieceCount[White][Bishop] * materialValues[Bishop] +
533 chessState.pieceCount[White][Rook] * materialValues[Rook] +
534 chessState.pieceCount[White][Queen] * materialValues[Queen];
535 chessState.materialValue[Black] =
536 chessState.pieceCount[Black][Pawn] * materialValues[Pawn] +
537 chessState.pieceCount[Black][Knight] * materialValues[Knight] +
538 chessState.pieceCount[Black][Bishop] * materialValues[Bishop] +
539 chessState.pieceCount[Black][Rook] * materialValues[Rook] +
540 chessState.pieceCount[Black][Queen] * materialValues[Queen];
542 chess2D.Update(null);
543 chess3D.Update(null);
545 if(chessState.isLocalPlayer[chessState.turn] && !local)
546 turnField.text = "Your turn";
547 stateField.text = "";
551 MakeMoveChar('e',2, 'e',4);
552 MakeMoveChar('a',7, 'a',6);
554 MakeMoveChar('d',2, 'd',4);
555 MakeMoveChar('f',7, 'f',5);
557 MakeMoveChar('e',4, 'f',5);
558 MakeMoveChar('b',8, 'c',6);
560 MakeMoveChar('d',4, 'd',5);
561 MakeMoveChar('c',6, 'e',5);
563 MakeMoveChar('f',2, 'f',4);
564 MakeMoveChar('e',5, 'f',7);
566 MakeMoveChar('g',1, 'f',3);
567 MakeMoveChar('c',7, 'c',5);
569 MakeMoveChar('c',2, 'c',4);
570 MakeMoveChar('g',7, 'g',6);
572 MakeMoveChar('f',5, 'g',6);
573 MakeMoveChar('f',7, 'd',6);
575 MakeMoveChar('g',6, 'h',7);
576 MakeMoveChar('g',8, 'h',6);
578 MakeMoveChar('f',1, 'd',3);
579 MakeMoveChar('d',6, 'f',7);
581 MakeMoveChar('c',1, 'd',2);
582 MakeMoveChar('d',8, 'b',6);
584 MakeMoveChar('d',2, 'c',3);
585 MakeMoveChar('b',6, 'd',6);
587 MakeMoveChar('c',3, 'h',8);
588 MakeMoveChar('f',7, 'h',8);
590 MakeMoveChar('d',1, 'd',2);
591 MakeMoveChar('e',7, 'e',6);
595 void MakeMoveChar(char x1, int y1, char x2, int y2)
597 MakeMove(x1 - 'a', y1 - 1, x2 - 'a', y2 - 1, Queen);
603 stopItem.disabled = !hosting;
604 disconnectItem.disabled = !sockets[SERVER_COLOR] && !sockets[CLIENT_COLOR];
605 hostItem.disabled = hosting;
607 endGameItem.disabled = !chessState.gameRunning;
613 for(c = 0; c<app.numDrivers; c++)
614 if(!strcmp(app.drivers[c], app.driver))
616 driverItems[c].checked = true;
621 // --- Chess Window Class ---
626 driverItems = new MenuItem[app.numDrivers];
627 for(c = 0; c<app.numDrivers; c++)
629 driverItems[c] = MenuItem { viewMenu, app.drivers[c], NotifySelect = SetDisplayDriver };
630 driverItems[c].id = c;
631 driverItems[c].isRadio = true;
633 // this.SetPalette(palette, true);
642 void DoEndGame(GameAction action)
645 if(sockets[SERVER_COLOR])
646 sockets[SERVER_COLOR].Disconnect(0);
647 else if(sockets[CLIENT_COLOR])
648 sockets[CLIENT_COLOR].Disconnect(0);
653 if(ai) aiThread.Abort();
655 chessState.gameRunning = false;
660 stateField.text = "";
664 case newAIGame: aiItem.NotifySelect(this, aiItem, 0); break;
665 case newLocalGame: localItem.NotifySelect(this, localItem, 0); break;
666 case close: Destroy(0); break;
670 GameAction nextAction;
672 bool EndGame(GameAction action)
674 if(chessState.gameRunning &&
675 (chessState.state == Normal || chessState.state == Check))
680 type = okCancel, contents = "Quit current game?",
681 master = this, text = "ECERE Chess";
684 void Chess::NotifyDestroyed(Window msgBox, DialogResult result)
687 DoEndGame(nextAction);
695 bool OnClose(bool parentClosing)
697 return EndGame(close);
703 delete sockets[Black];
704 delete sockets[White];
710 void Connect(const char * address)
712 ChessSocket socket { chess = this };
713 if(socket.Connect(address, CHESS_PORT))
715 sockets[SERVER_COLOR] = socket;
718 chessState.isLocalPlayer[CLIENT_COLOR] = true;
719 chessState.isLocalPlayer[SERVER_COLOR] = false;
725 // --- Chess Communications ---
727 class ChessSocket : Socket
730 property Chess chess { set { chess = value; } }
732 void OnDisconnect(int code)
734 if(this == chess.sockets[CLIENT_COLOR])
736 chess.sockets[CLIENT_COLOR] = null;
737 chess.chessState->gameRunning = false;
738 chess.turnField.text = "";
739 chess.stateField.text = "";
741 else if(this == chess.sockets[SERVER_COLOR])
743 chess.sockets[SERVER_COLOR] = null;
744 chess.chessState->gameRunning = false;
745 chess.turnField.text = "";
746 chess.stateField.text = "";
750 chess.chess2D.Update(null);
751 chess.chess3D.Update(null);
754 uint OnReceive(const byte * buffer, uint count)
756 if(count >= sizeof(ChessPacket))
758 ChessPacket packet = *(ChessPacket *)buffer;
762 chess.MakeMove(packet.x1, packet.y1, packet.x2, packet.y2, packet.promotion);
766 chess.chessState->gameRunning = true;
770 return sizeof(ChessPacket);
777 chess.sockets[SERVER_COLOR] = this;
778 chess.chessState->gameRunning = true;
784 class ChessService : Service
787 property Chess chess { set { chess = value; } }
791 if(!chess.chessState->gameRunning)
793 ChessPacket packet { type = NewGame };
795 chess.sockets[CLIENT_COLOR] = ChessSocket { this, chess = chess };
796 chess.sockets[CLIENT_COLOR].Send((byte *)&packet, sizeof(ChessPacket));
798 chess.chessState->isLocalPlayer[SERVER_COLOR] = true;
799 chess.chessState->isLocalPlayer[CLIENT_COLOR] = false;
802 chess.chessState->gameRunning = true;
804 chess.chess2D.Update(null);
805 chess.chess3D.Update(null);