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();
60 background = gray, hasMenuBar = true, hasStatusBar = true,
61 text = APPNAME, hasClose = true, hasMaximize = true, hasMinimize = true,
62 borderStyle = sizable, hasClose = true,
63 anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
65 bool hosting, local, ai;
66 Socket sockets[Player];
68 ChessService service { port = CHESS_PORT, chess = this };
70 MenuItem * driverItems;
72 ChessState chessState;
74 StatusField stateField { statusBar, width = 200 };
75 StatusField turnField { statusBar, width = 100 };
77 property ChessState * chessState { get { return &chessState; } }
79 // AI Player Threading
80 AIThread aiThread { chess = this };
87 isActiveClient = true,
91 borderStyle = sizable,
94 position = Point { x = 445 },
95 size = Size { 200, 450 }
101 text = "2D Chess Board",
102 chessState = &chessState
108 text = "3D Chess Board",
109 anchor = Anchor { left = 0.5, top = 0.5, right = 0, bottom = 0 },
110 chessState = &chessState,
117 Menu gameMenu { menu, "Game", g };
118 Menu networkMenu { menu, "Network", n };
119 Menu viewMenu { menu, "View", v };
120 Menu windowMenu { menu, "Window", w };
121 MenuDivider { menu };
122 Menu helpMenu { menu, "Help", h };
127 gameMenu, "New AI Game\tCtrl+N", n, ctrlN;
129 bool NotifySelect(MenuItem selection, Modifiers mods)
134 chessState.gameRunning = true;
136 chessState.isLocalPlayer[White] = true;
137 chessState.isLocalPlayer[Black] = false;
141 RandomSeed((int)(GetTime() * 10000));
151 gameMenu, "New Local Game\tCtrl+L", l, ctrlL;
152 bool NotifySelect(MenuItem selection, Modifiers mods)
157 chessState.gameRunning = true;
158 chessState.isLocalPlayer[White] = true;
159 chessState.isLocalPlayer[Black] = true;
169 gameMenu, "End Game", e;
170 bool NotifySelect(MenuItem selection, Modifiers mods)
177 MenuItem { gameMenu, "Exit\tAlt+F4", x, NotifySelect = MenuFileExit };
182 networkMenu, "Connect...", c;
183 bool NotifySelect(MenuItem selection, Modifiers mods)
189 ConnectDialog { master = this }.Modal();
195 MenuItem disconnectItem
197 networkMenu, "Disconnect", d, NotifySelect = endGameItem.NotifySelect
202 networkMenu, "Host", h;
203 bool NotifySelect(MenuItem selection, Modifiers mods)
219 networkMenu, "Stop Hosting", s;
220 bool NotifySelect(MenuItem selection, Modifiers mods)
233 MenuItem fullScreenItem
235 //viewMenu, "Full Screen", f, checkable = true;
236 bool NotifySelect(MenuItem selection, Modifiers mods)
238 app.fullScreen ^= true;
240 anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
245 bool SetDisplayDriver(MenuItem selection, Modifiers mods)
247 //app.driver = app.drivers[selection.id];
253 MenuItem { windowMenu, "Next\tF6", n, NotifySelect = MenuWindowNext };
254 MenuItem { windowMenu, "Previous\tShift-F6", p, NotifySelect = MenuWindowPrevious };
255 MenuDivider { windowMenu };
256 MenuItem { windowMenu, "Windows...", w, NotifySelect = MenuWindowWindows };
261 helpMenu, "About...\tF1", a, f1;
262 bool NotifySelect(MenuItem selection, Modifiers mods)
264 AboutChess { master = this }.Modal();
269 // --- Chess Utilities ---
270 bool MakeMove(int x1, int y1, int x2, int y2, PieceType promotion)
274 PieceType type = chessState.board[y1][x1].type;
275 Player player = chessState.board[y1][x1].player;
278 if(type == Pawn && y2 == ((player == White) ? 7 : 0))
280 if(chessState.isLocalPlayer[chessState.turn] &&
281 IsMoveValid(x1,y1,x2,y2, chessState, null, true))
283 chess2D.Update(null);
284 chess3D.Update(null);
285 promotion = (PieceType)Promotion { master = this }.Modal();
289 if(StateMakeMove(chessState, x1,y1,x2,y2, promotion, true, null))
293 if(chessState.isLocalPlayer[player] && !local && !ai)
303 promotion = promotion
305 sockets[player^(Player)1].Send((byte *)&packet, sizeof(ChessPacket));
310 moveList.AddStringf(" %c%c-%c%c",
311 x1+'a',y1+'1', x2+'a',y2+'1');
315 moveList.AddStringf("%3d. %c%c-%c%c",
316 chessState.numMoves/2+1, x1+'a',y1+'1', x2+'a',y2+'1');
323 GenerateMoveList(chessState, stack);
327 if(Check(chessState, chessState.turn, -1, -1))
331 chessState.state = Check; // TODO: Fix this
332 stateField.text = "Check!";
336 chessState.state = CheckMate;
337 if(chessState.turn == White)
338 stateField.text = "White are Checkmate.";
340 stateField.text = "Black are Checkmate.";
343 else if(!stack.count)
345 chessState.state = StaleMate;
346 stateField.text = "Stalemate.";
349 stateField.text = "";
351 // TOFIX: Gets confused with function!
352 if((chessState.state == Check || chessState.state == Normal) &&
353 chessState.isLocalPlayer[chessState.turn] && !local)
354 turnField.text = "Your turn";
360 chess2D.Update(null);
361 chess3D.Update(null);
365 void ProcessUserMove(int x1, int y1, int x2, int y2)
367 if(MakeMove(x1, y1, x2, y2, 0))
373 chess2D.Update(null);
375 chess3D.Update(null);
386 chessState.state = Normal;
387 chessState.numMoves = 0;
389 for(y = 0; y <8; y++)
391 chessState.board[y][x] = Piece {};
397 chessState.board[1][x] = Piece { White, Pawn };
398 chessState.board[0][0] = chessState.board[0][7] = Piece { White, Rook };
399 chessState.board[0][1] = chessState.board[0][6] = Piece { White, Knight };
400 chessState.board[0][2] = chessState.board[0][5] = Piece { White, Bishop };
401 chessState.board[0][3] = Piece { White, Queen };
403 chessState.board[0][4] = Piece { White, King };
408 chessState.board[6][x] = Piece { Black, Pawn };
409 chessState.board[7][0] = chessState.board[7][7] = Piece { Black, Rook };
410 chessState.board[7][1] = chessState.board[7][6] = Piece { Black, Knight };
411 chessState.board[7][2] = chessState.board[7][5] = Piece { Black, Bishop };
413 chessState.board[7][3] = Piece { Black, Queen };
414 chessState.board[7][4] = Piece { Black, King };
418 chessState.kingMoved[Black] = chessState.kingMoved[White] = false;
419 chessState.kRookMoved[Black] = chessState.kRookMoved[White] = false;
420 chessState.qRookMoved[Black] = chessState.qRookMoved[White] = false;
422 chessState.kRookMoved[Black] = chessState.kRookMoved[White] = true;
423 chessState.qRookMoved[Black] = chessState.qRookMoved[White] = true;
427 chessState.kings[Black].x = chessState.kings[White].x = 4;
428 chessState.kings[Black].y = 7;
429 chessState.kings[White].y = 0;
432 chessState.kings[Black].x = 4;
433 chessState.kings[Black].y = 6;
434 chessState.kings[White].x = 4;
435 chessState.kings[White].y = 0;
437 chessState.kingMoved[0] = false;
438 chessState.kingMoved[1] = true;
439 chessState.kRookMoved[0] = false;
440 chessState.qRookMoved[0] = false;
441 chessState.kRookMoved[1] = true;
442 chessState.qRookMoved[1] = true;
444 chessState.board[0][0] = Piece { White, Rook };
445 chessState.board[0][4] = Piece { White, King };
446 chessState.board[0][7] = Piece { White, Rook };
447 chessState.board[1][0] = Piece { White, Pawn };
448 chessState.board[1][2] = Piece { White, Pawn };
449 chessState.board[1][5] = Piece { White, Pawn };
450 chessState.board[1][6] = Piece { White, Pawn };
451 chessState.board[1][7] = Piece { White, Pawn };
452 chessState.board[2][1] = Piece { White, Pawn };
453 chessState.board[2][2] = Piece { White, Knight };
454 chessState.board[4][3] = Piece { White, Pawn };
455 chessState.board[5][6] = Piece { White, Bishop };
457 chessState.board[5][0] = Piece { Black, Pawn };
458 chessState.board[5][1] = Piece { Black, Pawn };
459 chessState.board[5][3] = Piece { Black, Knight };
460 chessState.board[5][5] = Piece { Black, Pawn };
461 chessState.board[5][7] = Piece { Black, Bishop };
462 chessState.board[6][0] = Piece { White, Rook };
463 chessState.board[6][2] = Piece { Black, Pawn };
464 chessState.board[6][3] = Piece { Black, Pawn };
465 chessState.board[6][4] = Piece { Black, King };
466 chessState.board[7][1] = Piece { Black, Knight };
467 chessState.board[7][2] = Piece { Black, Bishop };
469 chessState.turn = White;
471 chessState.castled[White] =
472 chessState.castled[Black] = false;
475 chessState.enPassant.x = -1;
476 chessState.enPassant.y = -1;
478 chessState.pieceCount[White][Pawn] = chessState.pieceCount[Black][Pawn] = 8;
479 chessState.pieceCount[White][Knight] = chessState.pieceCount[Black][Knight] = 2;
480 chessState.pieceCount[White][Bishop] = chessState.pieceCount[Black][Bishop] = 2;
481 chessState.pieceCount[White][Rook] = chessState.pieceCount[Black][Rook] = 2;
482 chessState.pieceCount[White][Queen] = chessState.pieceCount[Black][Queen] = 1;
483 chessState.pieceCount[White][King] = chessState.pieceCount[Black][King] = 1;
485 chessState.materialValue[White] =
486 chessState.pieceCount[White][Pawn] * materialValues[Pawn] +
487 chessState.pieceCount[White][Knight] * materialValues[Knight] +
488 chessState.pieceCount[White][Bishop] * materialValues[Bishop] +
489 chessState.pieceCount[White][Rook] * materialValues[Rook] +
490 chessState.pieceCount[White][Queen] * materialValues[Queen];
491 chessState.materialValue[Black] =
492 chessState.pieceCount[Black][Pawn] * materialValues[Pawn] +
493 chessState.pieceCount[Black][Knight] * materialValues[Knight] +
494 chessState.pieceCount[Black][Bishop] * materialValues[Bishop] +
495 chessState.pieceCount[Black][Rook] * materialValues[Rook] +
496 chessState.pieceCount[Black][Queen] * materialValues[Queen];
498 chess2D.Update(null);
499 chess3D.Update(null);
501 if(chessState.isLocalPlayer[chessState.turn] && !local)
502 turnField.text = "Your turn";
503 stateField.text = "";
507 MakeMoveChar('e',2, 'e',4);
508 MakeMoveChar('a',7, 'a',6);
510 MakeMoveChar('d',2, 'd',4);
511 MakeMoveChar('f',7, 'f',5);
513 MakeMoveChar('e',4, 'f',5);
514 MakeMoveChar('b',8, 'c',6);
516 MakeMoveChar('d',4, 'd',5);
517 MakeMoveChar('c',6, 'e',5);
519 MakeMoveChar('f',2, 'f',4);
520 MakeMoveChar('e',5, 'f',7);
522 MakeMoveChar('g',1, 'f',3);
523 MakeMoveChar('c',7, 'c',5);
525 MakeMoveChar('c',2, 'c',4);
526 MakeMoveChar('g',7, 'g',6);
528 MakeMoveChar('f',5, 'g',6);
529 MakeMoveChar('f',7, 'd',6);
531 MakeMoveChar('g',6, 'h',7);
532 MakeMoveChar('g',8, 'h',6);
534 MakeMoveChar('f',1, 'd',3);
535 MakeMoveChar('d',6, 'f',7);
537 MakeMoveChar('c',1, 'd',2);
538 MakeMoveChar('d',8, 'b',6);
540 MakeMoveChar('d',2, 'c',3);
541 MakeMoveChar('b',6, 'd',6);
543 MakeMoveChar('c',3, 'h',8);
544 MakeMoveChar('f',7, 'h',8);
546 MakeMoveChar('d',1, 'd',2);
547 MakeMoveChar('e',7, 'e',6);
551 void MakeMoveChar(char x1, int y1, char x2, int y2)
553 MakeMove(x1 - 'a', y1 - 1, x2 - 'a', y2 - 1, Queen);
558 stopItem.disabled = !hosting;
559 disconnectItem.disabled = !sockets[SERVER_COLOR] && !sockets[CLIENT_COLOR];
560 endGameItem.disabled = !chessState.gameRunning;
561 hostItem.disabled = hosting;
567 for(c = 0; c<app.numDrivers; c++)
568 if(!strcmp(app.drivers[c], app.driver))
570 driverItems[c].checked = true;
575 // --- Chess Window Class ---
580 driverItems = new MenuItem[app.numDrivers];
581 for(c = 0; c<app.numDrivers; c++)
583 driverItems[c] = MenuItem { viewMenu, app.drivers[c], NotifySelect = SetDisplayDriver };
584 driverItems[c].id = c;
585 driverItems[c].isRadio = true;
587 // this.SetPalette(palette, true);
598 if(chessState.gameRunning &&
599 (chessState.state == Normal || chessState.state == Check))
601 if(MessageBox { type = okCancel, contents = "Quit current game?",
602 master = this, text = "ECERE Chess" }.Modal() == cancel)
605 if(sockets[SERVER_COLOR])
606 sockets[SERVER_COLOR].Disconnect(0);
607 else if(sockets[CLIENT_COLOR])
608 sockets[CLIENT_COLOR].Disconnect(0);
611 if(ai) aiThread.Abort();
613 chessState.gameRunning = false;
618 stateField.text = "";
623 bool OnClose(bool parentClosing)
630 delete sockets[Black];
631 delete sockets[White];
635 void Connect(char * address)
637 ChessSocket socket { chess = this };
638 if(socket.Connect(address, CHESS_PORT))
640 sockets[SERVER_COLOR] = socket;
643 chessState.isLocalPlayer[CLIENT_COLOR] = true;
644 chessState.isLocalPlayer[SERVER_COLOR] = false;
649 // --- Chess Communications ---
650 class ChessSocket : Socket
653 property Chess chess { set { chess = value; } }
655 void OnDisconnect(int code)
657 if(this == chess.sockets[CLIENT_COLOR])
659 chess.sockets[CLIENT_COLOR] = null;
660 chess.chessState->gameRunning = false;
661 chess.turnField.text = "";
662 chess.stateField.text = "";
664 else if(this == chess.sockets[SERVER_COLOR])
666 chess.sockets[SERVER_COLOR] = null;
667 chess.chessState->gameRunning = false;
668 chess.turnField.text = "";
669 chess.stateField.text = "";
673 chess.chess2D.Update(null);
674 chess.chess3D.Update(null);
677 uint OnReceive(const byte * buffer, uint count)
679 if(count >= sizeof(ChessPacket))
681 ChessPacket packet = *(ChessPacket *)buffer;
685 chess.MakeMove(packet.x1, packet.y1, packet.x2, packet.y2, packet.promotion);
689 chess.chessState->gameRunning = true;
693 return sizeof(ChessPacket);
700 chess.sockets[SERVER_COLOR] = this;
701 chess.chessState->gameRunning = true;
707 class ChessService : Service
710 property Chess chess { set { chess = value; } }
714 if(!chess.chessState->gameRunning)
716 ChessPacket packet { type = NewGame };
718 chess.sockets[CLIENT_COLOR] = ChessSocket { this, chess = chess };
719 chess.sockets[CLIENT_COLOR].Send((byte *)&packet, sizeof(ChessPacket));
721 chess.chessState->isLocalPlayer[SERVER_COLOR] = true;
722 chess.chessState->isLocalPlayer[CLIENT_COLOR] = false;
725 chess.chessState->gameRunning = true;
727 chess.chess2D.Update(null);
728 chess.chess3D.Update(null);