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();
59 define stateWidth = 300;
60 define turnWidth = 150;
62 define stateWidth = 200;
63 define turnWidth = 100;
68 background = gray, hasMenuBar = true, hasStatusBar = true,
71 hasClose = true, hasMaximize = true, hasMinimize = true,
72 borderStyle = sizable,
74 anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
76 bool hosting, local, ai;
77 Socket sockets[Player];
79 ChessService service { port = CHESS_PORT, chess = this };
81 MenuItem * driverItems;
83 ChessState chessState;
85 StatusField stateField { statusBar, width = stateWidth};
86 StatusField turnField { statusBar, width = turnWidth };
88 property ChessState * chessState { get { return &chessState; } }
90 // AI Player Threading
91 AIThread aiThread { chess = this };
98 isActiveClient = true,
102 borderStyle = sizable,
103 hasVertScroll = true,
104 hasHorzScroll = true,
105 position = Point { x = 445 },
106 size = Size { 200, 450 }
112 text = "2D Chess Board",
113 chessState = &chessState
119 text = "3D Chess Board",
120 anchor = Anchor { left = 0.5, top = 0.5, right = 0, bottom = 0 },
121 chessState = &chessState,
128 Menu gameMenu { menu, "Game", g };
129 Menu networkMenu { menu, "Network", n };
130 Menu viewMenu { menu, "View", v };
131 Menu windowMenu { menu, "Window", w };
132 MenuDivider { menu };
133 Menu helpMenu { menu, "Help", h };
138 gameMenu, "New AI Game\tCtrl+N", n, ctrlN;
140 bool NotifySelect(MenuItem selection, Modifiers mods)
145 chessState.gameRunning = true;
147 chessState.isLocalPlayer[White] = true;
148 chessState.isLocalPlayer[Black] = false;
152 RandomSeed((int)(GetTime() * 10000));
162 gameMenu, "New Local Game\tCtrl+L", l, ctrlL;
163 bool NotifySelect(MenuItem selection, Modifiers mods)
168 chessState.gameRunning = true;
169 chessState.isLocalPlayer[White] = true;
170 chessState.isLocalPlayer[Black] = true;
180 gameMenu, "End Game", e;
181 bool NotifySelect(MenuItem selection, Modifiers mods)
188 MenuItem { gameMenu, "Exit\tAlt+F4", x, NotifySelect = MenuFileExit };
193 networkMenu, "Connect...", c;
194 bool NotifySelect(MenuItem selection, Modifiers mods)
200 ConnectDialog { master = this }.Modal();
206 MenuItem disconnectItem
208 networkMenu, "Disconnect", d, NotifySelect = endGameItem.NotifySelect
213 networkMenu, "Host", h;
214 bool NotifySelect(MenuItem selection, Modifiers mods)
230 networkMenu, "Stop Hosting", s;
231 bool NotifySelect(MenuItem selection, Modifiers mods)
244 MenuItem fullScreenItem
246 //viewMenu, "Full Screen", f, checkable = true;
247 bool NotifySelect(MenuItem selection, Modifiers mods)
249 app.fullScreen ^= true;
251 anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
256 bool SetDisplayDriver(MenuItem selection, Modifiers mods)
258 //app.driver = app.drivers[selection.id];
264 MenuItem { windowMenu, "Next\tF6", n, NotifySelect = MenuWindowNext };
265 MenuItem { windowMenu, "Previous\tShift-F6", p, NotifySelect = MenuWindowPrevious };
266 MenuDivider { windowMenu };
267 MenuItem { windowMenu, "Windows...", w, NotifySelect = MenuWindowWindows };
272 helpMenu, "About...\tF1", a, f1;
273 bool NotifySelect(MenuItem selection, Modifiers mods)
275 AboutChess { master = this }.Modal();
280 // --- Chess Utilities ---
281 bool MakeMove(int x1, int y1, int x2, int y2, PieceType promotion)
285 PieceType type = chessState.board[y1][x1].type;
286 Player player = chessState.board[y1][x1].player;
289 if(type == Pawn && y2 == ((player == White) ? 7 : 0))
291 if(chessState.isLocalPlayer[chessState.turn] &&
292 IsMoveValid(x1,y1,x2,y2, chessState, null, true))
294 chess2D.Update(null);
295 chess3D.Update(null);
296 promotion = (PieceType)Promotion { master = this }.Modal();
300 if(StateMakeMove(chessState, x1,y1,x2,y2, promotion, true, null))
304 if(chessState.isLocalPlayer[player] && !local && !ai)
314 promotion = promotion
316 sockets[player^(Player)1].Send((byte *)&packet, sizeof(ChessPacket));
321 moveList.AddStringf(" %c%c-%c%c",
322 x1+'a',y1+'1', x2+'a',y2+'1');
326 moveList.AddStringf("%3d. %c%c-%c%c",
327 chessState.numMoves/2+1, x1+'a',y1+'1', x2+'a',y2+'1');
334 GenerateMoveList(chessState, stack);
338 if(Check(chessState, chessState.turn, -1, -1))
342 chessState.state = Check; // TODO: Fix this
343 stateField.text = "Check!";
347 chessState.state = CheckMate;
348 if(chessState.turn == White)
349 stateField.text = "White are Checkmate.";
351 stateField.text = "Black are Checkmate.";
354 else if(!stack.count)
356 chessState.state = StaleMate;
357 stateField.text = "Stalemate.";
360 stateField.text = "";
362 // TOFIX: Gets confused with function!
363 if((chessState.state == Check || chessState.state == Normal) &&
364 chessState.isLocalPlayer[chessState.turn] && !local)
365 turnField.text = "Your turn";
371 chess2D.Update(null);
372 chess3D.Update(null);
376 void ProcessUserMove(int x1, int y1, int x2, int y2)
378 if(MakeMove(x1, y1, x2, y2, 0))
384 chess2D.Update(null);
386 chess3D.Update(null);
397 chessState.state = Normal;
398 chessState.numMoves = 0;
400 for(y = 0; y <8; y++)
402 chessState.board[y][x] = Piece {};
408 chessState.board[1][x] = Piece { White, Pawn };
409 chessState.board[0][0] = chessState.board[0][7] = Piece { White, Rook };
410 chessState.board[0][1] = chessState.board[0][6] = Piece { White, Knight };
411 chessState.board[0][2] = chessState.board[0][5] = Piece { White, Bishop };
412 chessState.board[0][3] = Piece { White, Queen };
414 chessState.board[0][4] = Piece { White, King };
419 chessState.board[6][x] = Piece { Black, Pawn };
420 chessState.board[7][0] = chessState.board[7][7] = Piece { Black, Rook };
421 chessState.board[7][1] = chessState.board[7][6] = Piece { Black, Knight };
422 chessState.board[7][2] = chessState.board[7][5] = Piece { Black, Bishop };
424 chessState.board[7][3] = Piece { Black, Queen };
425 chessState.board[7][4] = Piece { Black, King };
429 chessState.kingMoved[Black] = chessState.kingMoved[White] = false;
430 chessState.kRookMoved[Black] = chessState.kRookMoved[White] = false;
431 chessState.qRookMoved[Black] = chessState.qRookMoved[White] = false;
433 chessState.kRookMoved[Black] = chessState.kRookMoved[White] = true;
434 chessState.qRookMoved[Black] = chessState.qRookMoved[White] = true;
438 chessState.kings[Black].x = chessState.kings[White].x = 4;
439 chessState.kings[Black].y = 7;
440 chessState.kings[White].y = 0;
443 chessState.kings[Black].x = 4;
444 chessState.kings[Black].y = 6;
445 chessState.kings[White].x = 4;
446 chessState.kings[White].y = 0;
448 chessState.kingMoved[0] = false;
449 chessState.kingMoved[1] = true;
450 chessState.kRookMoved[0] = false;
451 chessState.qRookMoved[0] = false;
452 chessState.kRookMoved[1] = true;
453 chessState.qRookMoved[1] = true;
455 chessState.board[0][0] = Piece { White, Rook };
456 chessState.board[0][4] = Piece { White, King };
457 chessState.board[0][7] = Piece { White, Rook };
458 chessState.board[1][0] = Piece { White, Pawn };
459 chessState.board[1][2] = Piece { White, Pawn };
460 chessState.board[1][5] = Piece { White, Pawn };
461 chessState.board[1][6] = Piece { White, Pawn };
462 chessState.board[1][7] = Piece { White, Pawn };
463 chessState.board[2][1] = Piece { White, Pawn };
464 chessState.board[2][2] = Piece { White, Knight };
465 chessState.board[4][3] = Piece { White, Pawn };
466 chessState.board[5][6] = Piece { White, Bishop };
468 chessState.board[5][0] = Piece { Black, Pawn };
469 chessState.board[5][1] = Piece { Black, Pawn };
470 chessState.board[5][3] = Piece { Black, Knight };
471 chessState.board[5][5] = Piece { Black, Pawn };
472 chessState.board[5][7] = Piece { Black, Bishop };
473 chessState.board[6][0] = Piece { White, Rook };
474 chessState.board[6][2] = Piece { Black, Pawn };
475 chessState.board[6][3] = Piece { Black, Pawn };
476 chessState.board[6][4] = Piece { Black, King };
477 chessState.board[7][1] = Piece { Black, Knight };
478 chessState.board[7][2] = Piece { Black, Bishop };
480 chessState.turn = White;
482 chessState.castled[White] =
483 chessState.castled[Black] = false;
486 chessState.enPassant.x = -1;
487 chessState.enPassant.y = -1;
489 chessState.pieceCount[White][Pawn] = chessState.pieceCount[Black][Pawn] = 8;
490 chessState.pieceCount[White][Knight] = chessState.pieceCount[Black][Knight] = 2;
491 chessState.pieceCount[White][Bishop] = chessState.pieceCount[Black][Bishop] = 2;
492 chessState.pieceCount[White][Rook] = chessState.pieceCount[Black][Rook] = 2;
493 chessState.pieceCount[White][Queen] = chessState.pieceCount[Black][Queen] = 1;
494 chessState.pieceCount[White][King] = chessState.pieceCount[Black][King] = 1;
496 chessState.materialValue[White] =
497 chessState.pieceCount[White][Pawn] * materialValues[Pawn] +
498 chessState.pieceCount[White][Knight] * materialValues[Knight] +
499 chessState.pieceCount[White][Bishop] * materialValues[Bishop] +
500 chessState.pieceCount[White][Rook] * materialValues[Rook] +
501 chessState.pieceCount[White][Queen] * materialValues[Queen];
502 chessState.materialValue[Black] =
503 chessState.pieceCount[Black][Pawn] * materialValues[Pawn] +
504 chessState.pieceCount[Black][Knight] * materialValues[Knight] +
505 chessState.pieceCount[Black][Bishop] * materialValues[Bishop] +
506 chessState.pieceCount[Black][Rook] * materialValues[Rook] +
507 chessState.pieceCount[Black][Queen] * materialValues[Queen];
509 chess2D.Update(null);
510 chess3D.Update(null);
512 if(chessState.isLocalPlayer[chessState.turn] && !local)
513 turnField.text = "Your turn";
514 stateField.text = "";
518 MakeMoveChar('e',2, 'e',4);
519 MakeMoveChar('a',7, 'a',6);
521 MakeMoveChar('d',2, 'd',4);
522 MakeMoveChar('f',7, 'f',5);
524 MakeMoveChar('e',4, 'f',5);
525 MakeMoveChar('b',8, 'c',6);
527 MakeMoveChar('d',4, 'd',5);
528 MakeMoveChar('c',6, 'e',5);
530 MakeMoveChar('f',2, 'f',4);
531 MakeMoveChar('e',5, 'f',7);
533 MakeMoveChar('g',1, 'f',3);
534 MakeMoveChar('c',7, 'c',5);
536 MakeMoveChar('c',2, 'c',4);
537 MakeMoveChar('g',7, 'g',6);
539 MakeMoveChar('f',5, 'g',6);
540 MakeMoveChar('f',7, 'd',6);
542 MakeMoveChar('g',6, 'h',7);
543 MakeMoveChar('g',8, 'h',6);
545 MakeMoveChar('f',1, 'd',3);
546 MakeMoveChar('d',6, 'f',7);
548 MakeMoveChar('c',1, 'd',2);
549 MakeMoveChar('d',8, 'b',6);
551 MakeMoveChar('d',2, 'c',3);
552 MakeMoveChar('b',6, 'd',6);
554 MakeMoveChar('c',3, 'h',8);
555 MakeMoveChar('f',7, 'h',8);
557 MakeMoveChar('d',1, 'd',2);
558 MakeMoveChar('e',7, 'e',6);
562 void MakeMoveChar(char x1, int y1, char x2, int y2)
564 MakeMove(x1 - 'a', y1 - 1, x2 - 'a', y2 - 1, Queen);
569 stopItem.disabled = !hosting;
570 disconnectItem.disabled = !sockets[SERVER_COLOR] && !sockets[CLIENT_COLOR];
571 endGameItem.disabled = !chessState.gameRunning;
572 hostItem.disabled = hosting;
578 for(c = 0; c<app.numDrivers; c++)
579 if(!strcmp(app.drivers[c], app.driver))
581 driverItems[c].checked = true;
586 // --- Chess Window Class ---
591 driverItems = new MenuItem[app.numDrivers];
592 for(c = 0; c<app.numDrivers; c++)
594 driverItems[c] = MenuItem { viewMenu, app.drivers[c], NotifySelect = SetDisplayDriver };
595 driverItems[c].id = c;
596 driverItems[c].isRadio = true;
598 // this.SetPalette(palette, true);
609 if(chessState.gameRunning &&
610 (chessState.state == Normal || chessState.state == Check))
612 if(MessageBox { type = okCancel, contents = "Quit current game?",
613 master = this, text = "ECERE Chess" }.Modal() == cancel)
616 if(sockets[SERVER_COLOR])
617 sockets[SERVER_COLOR].Disconnect(0);
618 else if(sockets[CLIENT_COLOR])
619 sockets[CLIENT_COLOR].Disconnect(0);
622 if(ai) aiThread.Abort();
624 chessState.gameRunning = false;
629 stateField.text = "";
634 bool OnClose(bool parentClosing)
641 delete sockets[Black];
642 delete sockets[White];
646 void Connect(char * address)
648 ChessSocket socket { chess = this };
649 if(socket.Connect(address, CHESS_PORT))
651 sockets[SERVER_COLOR] = socket;
654 chessState.isLocalPlayer[CLIENT_COLOR] = true;
655 chessState.isLocalPlayer[SERVER_COLOR] = false;
660 // --- Chess Communications ---
661 class ChessSocket : Socket
664 property Chess chess { set { chess = value; } }
666 void OnDisconnect(int code)
668 if(this == chess.sockets[CLIENT_COLOR])
670 chess.sockets[CLIENT_COLOR] = null;
671 chess.chessState->gameRunning = false;
672 chess.turnField.text = "";
673 chess.stateField.text = "";
675 else if(this == chess.sockets[SERVER_COLOR])
677 chess.sockets[SERVER_COLOR] = null;
678 chess.chessState->gameRunning = false;
679 chess.turnField.text = "";
680 chess.stateField.text = "";
684 chess.chess2D.Update(null);
685 chess.chess3D.Update(null);
688 uint OnReceive(const byte * buffer, uint count)
690 if(count >= sizeof(ChessPacket))
692 ChessPacket packet = *(ChessPacket *)buffer;
696 chess.MakeMove(packet.x1, packet.y1, packet.x2, packet.y2, packet.promotion);
700 chess.chessState->gameRunning = true;
704 return sizeof(ChessPacket);
711 chess.sockets[SERVER_COLOR] = this;
712 chess.chessState->gameRunning = true;
718 class ChessService : Service
721 property Chess chess { set { chess = value; } }
725 if(!chess.chessState->gameRunning)
727 ChessPacket packet { type = NewGame };
729 chess.sockets[CLIENT_COLOR] = ChessSocket { this, chess = chess };
730 chess.sockets[CLIENT_COLOR].Send((byte *)&packet, sizeof(ChessPacket));
732 chess.chessState->isLocalPlayer[SERVER_COLOR] = true;
733 chess.chessState->isLocalPlayer[CLIENT_COLOR] = false;
736 chess.chessState->gameRunning = true;
738 chess.chess2D.Update(null);
739 chess.chess3D.Update(null);