added .gitignore for all to enjoy
[chess] / src / chess.ec
1 /****************************************************************************
2    CHESS Game
3
4    Copyright (c) 2001 Jerome Jacovella-St-Louis
5    All Rights Reserved.
6    
7    chess.ec - Chess Main Window
8 ****************************************************************************/
9 #ifdef ECERE_STATIC
10 import static "ecere"
11 #else
12 import "ecere"
13 #endif
14 import "chess2D.ec"
15 import "chess3D.ec"
16 import "promotion.ec"
17 import "connect.ec"
18 import "about.ec"
19 import "ai.ec"
20 import "chessutils.ec"
21
22 // --- Definitions ---
23 define APPNAME = "ECERE Chess v0.4";
24 define CHESS_PORT = 7778;
25 define SERVER_COLOR = Black;
26 define CLIENT_COLOR = White;
27
28 // Network Messages
29 enum ChessMessage : byte { NewGame = 1, Position = 2 };
30
31 struct ChessPacket
32 {
33    ChessMessage type;
34    Player player;
35    byte x1,y1,x2,y2;
36    PieceType promotion;
37 };
38
39 ChessApp app;
40
41 class ChessApp : GuiApplication
42 {
43    appName = APPNAME;
44 #if defined(__WIN32__)
45    driver = "Direct3D";
46 #else
47    driver = "OpenGL";
48 #endif
49    Chess{};
50
51    void Main()
52    {
53       app = this;
54       GuiApplication::Main();
55    }
56 }
57
58 class Chess : Window
59 {
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 };
64
65    bool hosting, local, ai;
66    Socket sockets[Player];
67
68    ChessService service { port = CHESS_PORT, chess = this };
69
70    MenuItem * driverItems;
71
72    ChessState chessState;
73    
74    StatusField stateField { statusBar, width = 200 };
75    StatusField turnField { statusBar, width = 100 };
76
77    property ChessState * chessState { get { return &chessState; } }
78
79    // AI Player Threading
80    AIThread aiThread { chess = this };
81
82    // Windows
83    
84    ListBox moveList
85    {
86       parent = this,
87       isActiveClient = true,
88       text = "MoveList",
89       autoScroll = true,
90       hasMinimize = true,
91       borderStyle = sizable,
92       hasVertScroll = true,
93       hasHorzScroll = true,
94       position = Point { x = 445 },
95       size = Size { 200, 450 }
96    };
97
98    Chess2D chess2D
99    {
100       parent = this,
101       text = "2D Chess Board",
102       chessState = &chessState
103    };
104     
105    Chess3D chess3D
106    {
107       parent = this,
108       text = "3D Chess Board",
109       anchor = Anchor { left = 0.5, top = 0.5, right = 0, bottom = 0 },
110       chessState = &chessState,
111       state = maximized
112    };
113
114    // Main Menu
115    menu = Menu {};
116
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 };
123
124    // Game Menu
125    MenuItem aiItem
126    {
127       gameMenu, "New AI Game\tCtrl+N", n, ctrlN;
128
129       bool NotifySelect(MenuItem selection, Modifiers mods)
130       {
131          if(EndGame())
132          {
133             ai = true;
134             chessState.gameRunning = true;
135
136             chessState.isLocalPlayer[White] = true;
137             chessState.isLocalPlayer[Black] = false;
138
139             EnableMenus();
140          
141             RandomSeed((int)(GetTime() * 10000));
142          
143             NewGame();
144          }
145          return true;
146       }
147    };
148
149    MenuItem localItem
150    {
151       gameMenu, "New Local Game\tCtrl+L", l, ctrlL;
152       bool NotifySelect(MenuItem selection, Modifiers mods)
153       {
154          if(EndGame())
155          {
156             local = true;
157             chessState.gameRunning = true;
158             chessState.isLocalPlayer[White] = true;
159             chessState.isLocalPlayer[Black] = true;
160             EnableMenus();
161             NewGame();
162          }
163          return true;
164       }
165    };
166
167    MenuItem endGameItem
168    {
169       gameMenu, "End Game", e;
170       bool NotifySelect(MenuItem selection, Modifiers mods)
171       {
172          EndGame();
173          return true;
174       }
175    };
176
177    MenuItem { gameMenu, "Exit\tAlt+F4", x, NotifySelect = MenuFileExit };
178
179    // Network Menu
180    MenuItem connectItem
181    {
182       networkMenu, "Connect...", c;
183       bool NotifySelect(MenuItem selection, Modifiers mods)
184       {
185          if(EndGame())
186          {
187             hosting = false;
188             service.Stop();
189             ConnectDialog { master = this }.Modal();
190          }
191          return true;
192       }
193    };
194
195    MenuItem disconnectItem
196    {
197       networkMenu, "Disconnect", d, NotifySelect = endGameItem.NotifySelect
198    };
199
200    MenuItem hostItem
201    {
202       networkMenu, "Host", h;
203       bool NotifySelect(MenuItem selection, Modifiers mods)
204       {
205          if(EndGame())
206          {
207             if(service.Start())
208             {
209                hosting = true;
210                EnableMenus();
211             }
212          }
213          return true;
214       }
215    };
216
217    MenuItem stopItem
218    {
219       networkMenu, "Stop Hosting", s;
220       bool NotifySelect(MenuItem selection, Modifiers mods)
221       {
222          if(EndGame())
223          {
224             hosting = false;
225             service.Stop();
226             EnableMenus();
227          }
228          return true;
229       }
230    };
231
232    // View Menu
233    MenuItem fullScreenItem
234    {
235       //viewMenu, "Full Screen", f, checkable = true;
236       bool NotifySelect(MenuItem selection, Modifiers mods)
237       {
238          app.fullScreen ^= true;
239          SetDriver();
240          anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
241          return true;
242       }
243    };
244
245    bool SetDisplayDriver(MenuItem selection, Modifiers mods)
246    {
247       //app.driver = app.drivers[selection.id];
248       SetDriver();
249       return true;
250    }
251
252    // Window Menu
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 };
257
258    //  Help Menu
259    MenuItem aboutItem
260    { 
261       helpMenu, "About...\tF1", a, f1;
262       bool NotifySelect(MenuItem selection, Modifiers mods)
263       {
264          AboutChess { master = this }.Modal();
265          return true;
266       }
267    };
268          
269    // --- Chess Utilities ---
270    bool MakeMove(int x1, int y1, int x2, int y2, PieceType promotion)
271    {
272       bool valid = false;
273       
274       PieceType type = chessState.board[y1][x1].type;
275       Player player = chessState.board[y1][x1].player;
276
277       // Pawn Promotion
278       if(type == Pawn && y2 == ((player == White) ? 7 : 0))
279       {
280          if(chessState.isLocalPlayer[chessState.turn] &&
281             IsMoveValid(x1,y1,x2,y2, chessState, null, true))
282          {
283             chess2D.Update(null);
284             chess3D.Update(null);
285             promotion = (PieceType)Promotion { master = this }.Modal();
286          }
287       }
288       
289       if(StateMakeMove(chessState, x1,y1,x2,y2, promotion, true, null))
290       {
291          valid = true;
292          
293          if(chessState.isLocalPlayer[player] && !local && !ai)
294          {
295             ChessPacket packet
296             { 
297                type = Position,
298                player = player,
299                x1 = (byte)x1,
300                y1 = (byte)y1,
301                x2 = (byte)x2,
302                y2 = (byte)y2,
303                promotion = promotion
304             };
305             sockets[player^(Player)1].Send((byte *)&packet, sizeof(ChessPacket));
306          }
307
308          if(player == Black)
309          {
310             moveList.AddStringf("               %c%c-%c%c",
311                x1+'a',y1+'1', x2+'a',y2+'1');
312          }
313          else
314          {
315             moveList.AddStringf("%3d. %c%c-%c%c",
316                chessState.numMoves/2+1, x1+'a',y1+'1', x2+'a',y2+'1');
317          }
318
319
320          // Update Status Bar
321          {
322             MoveStack stack { };
323             GenerateMoveList(chessState, stack);
324
325             delete stack.moves;
326             
327             if(Check(chessState, chessState.turn, -1, -1))
328             {
329                if(stack.count)
330                {
331                   chessState.state = Check; // TODO: Fix this
332                   stateField.text = "Check!";
333                }
334                else
335                {
336                   chessState.state = CheckMate;
337                   if(chessState.turn == White)
338                      stateField.text = "White are Checkmate.";
339                   else
340                      stateField.text = "Black are Checkmate.";
341                }
342             }
343             else if(!stack.count)
344             {
345                chessState.state = StaleMate;
346                stateField.text = "Stalemate.";
347             }
348             else
349                stateField.text = "";
350
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";
355             else
356                turnField.text = "";
357          }
358       }
359    
360       chess2D.Update(null);
361       chess3D.Update(null);
362       return valid;   
363    }
364
365    void ProcessUserMove(int x1, int y1, int x2, int y2)
366    {
367       if(MakeMove(x1, y1, x2, y2, 0))
368       {
369          if(ai)
370             aiThread.Play();
371       }
372
373       chess2D.Update(null);
374
375       chess3D.Update(null);
376    }
377
378    // #define QUEENMATE
379
380    void NewGame()
381    {
382       int x,y;
383
384       moveList.Clear();
385
386       chessState.state = Normal;
387       chessState.numMoves = 0;
388
389       for(y = 0; y <8; y++)
390          for(x = 0; x<8; x++)
391             chessState.board[y][x] = Piece {};
392
393       // WHITE PIECES
394
395    #ifndef QUEENMATE
396       for(x = 0; x<8; x++)
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 };
402    #endif
403       chessState.board[0][4] = Piece { White, King };
404
405       // BLACK PIECES
406    #ifndef QUEENMATE
407       for(x = 0; x<8; x++)
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 };
412    #endif
413       chessState.board[7][3] = Piece { Black, Queen };
414       chessState.board[7][4] = Piece { Black, King };
415
416       // CASTLE STATUS
417    #ifndef QUEENMATE
418       chessState.kingMoved[Black] = chessState.kingMoved[White] = false;
419       chessState.kRookMoved[Black] = chessState.kRookMoved[White] = false;
420       chessState.qRookMoved[Black] = chessState.qRookMoved[White] = false;
421    #else
422       chessState.kRookMoved[Black] = chessState.kRookMoved[White] = true;
423       chessState.qRookMoved[Black] = chessState.qRookMoved[White] = true;
424    #endif
425
426       // KING POSITION
427       chessState.kings[Black].x = chessState.kings[White].x = 4;
428       chessState.kings[Black].y = 7;
429       chessState.kings[White].y = 0;
430
431    #if 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;
436
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;
443
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 };
456
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 };
468    #endif
469       chessState.turn = White;
470
471       chessState.castled[White] = 
472       chessState.castled[Black] = false;
473
474       // EN PASSANT STATUS
475       chessState.enPassant.x = -1;
476       chessState.enPassant.y = -1;
477
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;
484
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];
497
498       chess2D.Update(null);
499       chess3D.Update(null);
500
501       if(chessState.isLocalPlayer[chessState.turn] && !local)
502          turnField.text = "Your turn";
503       stateField.text = "";
504
505    /*
506       // 1
507       MakeMoveChar('e',2, 'e',4);
508       MakeMoveChar('a',7, 'a',6);
509       // 2
510       MakeMoveChar('d',2, 'd',4);
511       MakeMoveChar('f',7, 'f',5);
512       // 3
513       MakeMoveChar('e',4, 'f',5);
514       MakeMoveChar('b',8, 'c',6);
515       // 4
516       MakeMoveChar('d',4, 'd',5);
517       MakeMoveChar('c',6, 'e',5);
518       // 5
519       MakeMoveChar('f',2, 'f',4);
520       MakeMoveChar('e',5, 'f',7);
521       // 6
522       MakeMoveChar('g',1, 'f',3);
523       MakeMoveChar('c',7, 'c',5);
524       // 7
525       MakeMoveChar('c',2, 'c',4);
526       MakeMoveChar('g',7, 'g',6);
527       // 8
528       MakeMoveChar('f',5, 'g',6);
529       MakeMoveChar('f',7, 'd',6);
530       // 9
531       MakeMoveChar('g',6, 'h',7);
532       MakeMoveChar('g',8, 'h',6);
533       // 10
534       MakeMoveChar('f',1, 'd',3);
535       MakeMoveChar('d',6, 'f',7);
536       // 11
537       MakeMoveChar('c',1, 'd',2);
538       MakeMoveChar('d',8, 'b',6);
539       // 12
540       MakeMoveChar('d',2, 'c',3);
541       MakeMoveChar('b',6, 'd',6);
542       // 13
543       MakeMoveChar('c',3, 'h',8);
544       MakeMoveChar('f',7, 'h',8);
545       // 14
546       MakeMoveChar('d',1, 'd',2);
547       MakeMoveChar('e',7, 'e',6);      
548    */
549    }
550
551    void MakeMoveChar(char x1, int y1, char x2, int y2)
552    {
553       MakeMove(x1 - 'a', y1 - 1, x2 - 'a', y2 - 1, Queen);
554    }
555    
556    void EnableMenus()
557    {
558       stopItem.disabled = !hosting;
559       disconnectItem.disabled = !sockets[SERVER_COLOR] && !sockets[CLIENT_COLOR];
560       endGameItem.disabled = !chessState.gameRunning;
561       hostItem.disabled = hosting;
562    }
563
564    void SetDriver()
565    {
566       int c;
567       for(c = 0; c<app.numDrivers; c++)
568          if(!strcmp(app.drivers[c], app.driver))
569          {
570             driverItems[c].checked = true;
571             break;
572          }
573    }
574
575    // --- Chess Window Class ---
576    bool OnCreate()
577    {
578       int c;
579
580       driverItems = new MenuItem[app.numDrivers];
581       for(c = 0; c<app.numDrivers; c++)
582       {
583          driverItems[c] = MenuItem { viewMenu, app.drivers[c], NotifySelect = SetDisplayDriver };
584          driverItems[c].id = c;
585          driverItems[c].isRadio = true;         
586       }
587       // this.SetPalette(palette, true);
588
589       SetDriver();
590       NewGame();
591       EnableMenus();
592
593       return true;
594    }
595
596    bool EndGame()
597    {
598       if(chessState.gameRunning && 
599          (chessState.state == Normal || chessState.state == Check))
600       {
601          if(MessageBox { type = okCancel, contents = "Quit current game?", 
602             master = this, text = "ECERE Chess" }.Modal() == cancel)
603             return false;   
604       }
605       if(sockets[SERVER_COLOR])
606          sockets[SERVER_COLOR].Disconnect(0);
607       else if(sockets[CLIENT_COLOR])
608          sockets[CLIENT_COLOR].Disconnect(0);
609       else if(local || ai)
610       {
611          if(ai) aiThread.Abort();
612          local = ai = false;
613          chessState.gameRunning = false;
614       }
615       EnableMenus();
616
617       turnField.text = "";
618       stateField.text = "";
619
620       return true;
621    }
622
623    bool OnClose(bool parentClosing)
624    {
625       return EndGame();
626    }
627
628    void OnDestroy()
629    {
630       delete sockets[Black];
631       delete sockets[White];
632       delete driverItems;
633    }
634             
635    void Connect(char * address)
636    {
637       ChessSocket socket { chess = this };
638       if(socket.Connect(address, CHESS_PORT))
639       {
640          sockets[SERVER_COLOR] = socket;
641          EnableMenus();
642
643          chessState.isLocalPlayer[CLIENT_COLOR] = true;
644          chessState.isLocalPlayer[SERVER_COLOR] = false;
645       }
646    }
647 }
648
649 // --- Chess Communications ---
650 class ChessSocket : Socket
651 {
652    Chess chess;
653    property Chess chess { set { chess = value; } }
654
655    void OnDisconnect(int code)
656    {
657       if(this == chess.sockets[CLIENT_COLOR])
658       {
659          chess.sockets[CLIENT_COLOR] = null;
660          chess.chessState->gameRunning = false;
661          chess.turnField.text = "";
662          chess.stateField.text = "";
663       }
664       else if(this == chess.sockets[SERVER_COLOR])
665       {
666          chess.sockets[SERVER_COLOR] = null;
667          chess.chessState->gameRunning = false;
668          chess.turnField.text = "";
669          chess.stateField.text = "";
670       }
671
672       chess.EnableMenus();
673       chess.chess2D.Update(null);
674       chess.chess3D.Update(null);
675    }
676    
677    uint OnReceive(const byte * buffer, uint count)
678    {
679       if(count >= sizeof(ChessPacket))
680       {
681          ChessPacket packet = *(ChessPacket *)buffer;
682          switch(packet.type)
683          {
684             case Position:
685                chess.MakeMove(packet.x1, packet.y1, packet.x2, packet.y2, packet.promotion);
686                chess.Activate();
687                break;
688             case NewGame:
689                chess.chessState->gameRunning = true;
690                chess.NewGame();
691                break;
692          }
693          return sizeof(ChessPacket);
694       }
695       return 0;
696    }
697
698    void OnConnect()
699    {
700       chess.sockets[SERVER_COLOR] = this;
701       chess.chessState->gameRunning = true;
702       chess.EnableMenus();
703       chess.NewGame();
704    }
705 }
706
707 class ChessService : Service
708 {
709    Chess chess;
710    property Chess chess { set { chess = value; } }
711    
712    void OnAccept()
713    {
714       if(!chess.chessState->gameRunning)
715       {
716          ChessPacket packet { type = NewGame };
717
718          chess.sockets[CLIENT_COLOR] = ChessSocket { this, chess = chess };
719          chess.sockets[CLIENT_COLOR].Send((byte *)&packet, sizeof(ChessPacket));
720
721          chess.chessState->isLocalPlayer[SERVER_COLOR] = true;
722          chess.chessState->isLocalPlayer[CLIENT_COLOR] = false;
723
724          chess.NewGame();
725          chess.chessState->gameRunning = true;
726          chess.EnableMenus();
727          chess.chess2D.Update(null);
728          chess.chess3D.Update(null);
729       }
730    }
731 }