|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
SummaryHuo Chess is a chess program of just 56.5 KB in size. Its current version (Huo Chess v0.721) can think up to 8 moves depth (4 half-moves for white and 4 half-moves for black pieces) and has an opening book capability. Huo Chess plays decent chess and has managed to draw Microchess, the first microchess from the Commodore era (see the Huo Chess Games Archive below). Its algorithm can be used to study the underlying logic of a chess program or as a basis for your own chess program (the source code is heavily commented and easily customized). The source code is continually improving and is distributed under The Code Project Open License. In this article, I also analyze its programming logic, with emphasis on the general flow inside the computer’s "mind," from the initial scanning of the chessboard up to the final decision on which move to play. Any comments are welcome! VersioningHuo Chess is intelligently designed, but it also evolves. Currently, the program is at version 0.721. The timeline of versions and the respective changes conducted in the program's code are depicted below.
IntroductionWhat is most important when you program? Is it the program design? Yes? If "yes," then why is that every program today is bigger than 123876 GB and requires 120987 MB of RAM to run? Why is every new project and program — by default — larger in size and slower in speed than the previous version? The answer is simple: because of the increasing speed of computers, no one actually cares about optimizing the code structure and the software design, since the new processor will definitely make the program look fine for the end user. During the time of Commodore computers, memory was rather limited. That is why programmers tried to make the code small and efficient. This resulted in Microchess, which was a very efficient, small (as the name "micro," which means "small" in Greek, implies) chess program that was able to play chess in the few KB available in computers of that time. What I AccomplishedDriven by the above, I started developing a program in CLI C++ v8.0 (with Visual Studio 2008 Express Edition) to play chess with the intention of making a small in size (Micro)chess, even smaller than the Commodore-era Microchess. I named it "Huo Chess" for personal reasons. The program plays decent chess, but unfortunately will probably lose if it plays with Garry Kasparov. Its size is currently 56.5 KB, while the respective emulation of Microchess in C is 56 KB. However, it must be noted that the original Microchess, written in pure Assembly, was about 1 KB…something that I believe no one will ever match! In matches against Microchess, version 0.4 has managed to draw Microchess (see below the section of Huo Chess Games Archive). How to Customize - Optimize the ProgramThe program has an opening book capability, which means that anyone can optimize the program by adding his/her own opening moves data in the respective folder Huo Chess Opening Book (which should be in the same directory with the executable). You can also add more thinking depth capability, by adding new I. Chess Algorithm AnalysisThe algorithm used in this program for the implementation of the computer thinking is the "Brute Force Algorithm." Huo Chess plays with the material in mind, while its code has some hints of positional strategic playing embedded. More analytically: When the program starts thinking, it scans the chessboard to find where its pieces are (see
More analytically, a high-level example of the progress of the main algorithm is as follows:
You can II. Huo Chess Thought FlowBelow, I analyze the thought flow of the chess program. I will describe only the main steps and code segments, so as to show the way the computer scans the chessboard, conducts all possible moves and finally finds the better one. The function names appear in bold, i.e. ComputerMove - Start indicates the beginning of the ComputerMove - Start
Call: CheckMove
10. HY_Kobei_Kommati = false;
11.
12. if((ProsorinoKommati->CompareTo("White Bishop") == 0) ||
13. (ProsorinoKommati->CompareTo("Black Bishop") == 0))
14. {
15. HY_Kobei_Kommati = true;
16. Kopsimo_Kommati_Human_value = 3;
17. }
18. elseif((ProsorinoKommati->CompareTo("White Knight") == 0) ||
19. (ProsorinoKommati->CompareTo("Black
20. Knight") == 0))
21. {
[...]
23. if((ProsorinoKommati->CompareTo("White Queen") == 0) || (
24. ProsorinoKommati->CompareTo("Black Queen") == 0))
25. this->eat_queen = true;
26. else this->eat_queen = false;
27. if(Move_Analyzed = Thinking_Depth)
28. {
29. // Measure the score of the move and record it as best move, if
30.
31. // it is larger than the score of the so-far best move score.
32.
33. }
34. if(Move_Analyzed <Thinking_Depth)
35. {
36. Human_is_in_check = false;
37.
38. WhiteKingCheck = CheckForWhiteCheck(CMSkakiera);
39. if( (this->m_PlayerColor->CompareTo("White") == 0) && (
40. WhiteKingCheck == true) )
41. Human_is_in_check = true;
42.
43. BlackKingCheck = CheckForBlackCheck(CMSkakiera);
44.
45. if( (this->m_PlayerColor->CompareTo("Black") == 0) && (
46. BlackKingCheck == true) )
47. Human_is_in_check = true;
48.
49. Move_Analyzed = Move_Analyzed + 1;
50.
51. Who_Is_Analyzed = "HY";
52.
53. for(i = 0; i <= 7; i++)
54. {
55. for(j = 0; j <= 7; j++)
56. {
57. Skakiera_Move_After[(i),(j)] = CMSkakiera[(i),(j)];
58. }
59. }
60. // Call HumanMove to find the best possible answer of
61.
62. // human opponent to the move currently analyzed
63.
64. // by the computer thought process
65.
66.
67. this->HumanMove(Skakiera_Move_After);
HumanMove
CheckHumanMove - StartvoidCheckHumanMove(array<String^, 2>^ CMSkakiera_Human_Thinking)
Measure move score (if it is legal and correct) -> record the move as "best" move if its score is larger than the so-far best possible human move. Human_Kobei_Kommati = false;
if((m_FinishingColumnNumber == this->m_FinishingColumnNumber_HY) && (
m_FinishingRank == this->m_FinishingRank_HY))
{
if((ProsorinoKommati->CompareTo("White Bishop") == 0) || (
ProsorinoKommati->CompareTo("Black Bishop") == 0))
{
Human_Kobei_Kommati = true;
[...]
Possible_mate = false;
if( Human_is_in_check == true)
{
WhiteKingCheck = CheckForWhiteCheck(CMSkakiera_Human_Thinking);
if( (this->m_PlayerColor->CompareTo("White") == 0) && (
WhiteKingCheck == true) )
Possible_mate = true;
BlackKingCheck = CheckForBlackCheck(CMSkakiera_Human_Thinking);
if( (this->m_PlayerColor->CompareTo("Black") == 0) && (
BlackKingCheck == true) )
Possible_mate = true;
}
CheckHumanMove - EndConduct the best human move found. Move_Analyzed = Move_Analyzed + 1;
Who_Is_Analyzed = "HY";
for(i = 0; i <= 7;i++)
{
for(j =0; j <= 7; j++)
{
Skakiera_Move_After[(i),(j)]=Skakiera_Human_Thinking[(i),(j)];
}
}
if(Move_Analyzed == 2)
this->ComputerMove2(Skakiera_Move_After);
elseif(Move_Analyzed == 4)
this->ComputerMove4(Skakiera_Move_After);
elseif(Move_Analyzed == 6)
this->ComputerMove6(Skakiera_Move_After);
elseif(Move_Analyzed == 8)
this->ComputerMove8(Skakiera_Move_After);
// Call ComputerMove2 to find the best next move of the HY (deeper thinking
ComputerMove2 - StartvoidComputerMove2(array<String^, 2>^ Skakiera_Thinking_2)
{
// Same as…ComputerMove
if(Move_Analyzed == 0)
{
// Same as…ComputerMove
// If we haven't reached the desired level of analysis, then the
// HumanMove will be called again, then again the ComputerMove
// function etc.
}
else
{
// Return to the ComputerMove function of the 'previous' thinking
// level to continue the analysis
Move_Analyzed = Move_Analyzed - 2;
Who_Is_Analyzed = "HY";
for(i = 0; i <= 7; i++)
{
for(j = 0; j <= 7; j++)
{
Skakiera_Thinking[i,j] = Skakiera_Move_0[i,j];
}
}
}
}
ComputerMove2 - EndHumanMove - Endif( this->Move_Analyzed == 0)
{
Kommati_moved = MovingPiece_HY;
STHSIMO = false;
FirstCall_CheckStisimoMove = true;
FirstCll_CheckStisimoMove_2 = true;
eat_queen = false;
hy_eats_the_piece = false;
human_eats_the_piece = false;
Who_Is_Analyzed = "Human";
CheckStisimo(CMSkakiera);
}
CheckMove - End// close for iii, jjj loops
// close if( Stop_Analyzing = false ) segment
14.ifMove_Analyzed = 0
{
20.}
21.else
22.{
23.// Return to the ComputerMove function of the 'previous' thinking
24.
25.// level to continue the analysis
26.
27.Move_Analyzed = Move_Analyzed - 2;
28.
29.Who_Is_Analyzed = "HY";
30.
31.for(i = 0; i <= 7; i++)
32.{
33.for(j = 0; j <= 7; j++)
34.{
35.Skakiera_Thinking[i,j] = Skakiera_Move_0[i,j];
36.}
37.}
}
ComputerMove – EndIII. Huo Chess Thought Flow ScenarioBelow, I illustrate the step-by-step process of the computer's thought for a thinking depth of 2. Let's see the "step" boxes to understand the way the program is structured. Scenario Details
ComputerMove - StartStep 1START
2. if( Move_Analyzed >Thinking_Depth )
3. Stop_Analyzing = true;
4. if( Stop_Analyzing = false)
for iii, jjj
Call: CheckMove - Start
Step 2IF result: FALSE
11. if(Move_Analyzed = Thinking_Depth)
Step 3IF result: TRUE
13. if(Move_Analyzed <Thinking_Depth)
14. {
15. Move_Analyzed = Move_Analyzed + 1;
16.
17. Who_Is_Analyzed = "HY";
18.
19. for(i = 0; i <= 7; i++)
20. {
21. for(j = 0; j <= 7; j++)
22. {
23. Skakiera_Move_After[(i),(j)] = CMSkakiera[(i),(j)];
24. }
25. }
26. // Call HumanMove to find the best possible answer of human opponent to
27.
28. // the move currently analyzed by the computer thought process
29.
30.
31. this->HumanMove(Skakiera_Move_After);
HumanMove - StartStep 4Find the best answer of the Human.
CheckHumanMove - StartvoidCheckHumanMove(array<String^, 2>^ CMSkakiera_Human_Thinking)
Count the score of the move and record it as "best" if its score is better than the so-far best move. CheckHumanMove - EndConduct the best move of the human. Move_Analyzed = Move_Analyzed + 1;
Who_Is_Analyzed = "HY";
for(i = 0; i <= 7;i++)
{
for(j = 0; j <= 7; j++)
{
Skakiera_Move_After[(i),(j)]=Skakiera_Human_Thinking[(i),(j)];
}
Step 5
Step 6CALL next
if(Move_Analyzed == 2)
this->ComputerMove2(Skakiera_Move_After);
elseif(Move_Analyzed == 4)
this->ComputerMove4(Skakiera_Move_After);
elseif(Move_Analyzed == 6)
this->ComputerMove6(Skakiera_Move_After);
elseif(Move_Analyzed == 8)
this->ComputerMove8(Skakiera_Move_After);
// Call ComputerMove2 to find the best next move of the HY (deeper thinking)
Step 7Scan the chessboard and find the best move for the computer.
voidComputerMove2(array<String^, 2>^ Skakiera_Thinking_2)
{
// Same as…ComputerMove
if(Move_Analyzed == 0)
{
// Same as…ComputerMove
// If we haven't reached the desired level of analysis, then the HumanMove
// will be called again, then again the ComputerMove function etc.
}
Step 8Return to a previous
else
{
// Return to the ComputerMove function of the 'previous' thinking
// level to continue the analysis
Move_Analyzed = Move_Analyzed - 2;
Who_Is_Analyzed = "HY";
for(i = 0; i <= 7; i++)
{
for(j = 0; j <= 7; j++)
{
Skakiera_Thinking[i,j] = Skakiera_Move_0[i,j];
}
}
}
ComputerMove2 - EndHumanMove - EndCheckMove - End// close for iii, jjj loop
// close if( Stop_Analyzing = false ) segment
Step 9Play the move with the highest score. Now it is the Human's turn to play. ifmove_analyzed=0
20. else
21. {
22. Move_Analyzed = Move_Analyzed - 2;
23. Who_Is_Analyzed = "HY";
24.
25. for(i = 0; i <= 7; i++)
26. {
27. for(j = 0; j <= 7;j++)
28. {
29. Skakiera_Thinking[i,j] = Skakiera_Move_0[i,j];
30. }
31. }
}
ComputerMove – EndIV. Flowchart SummaryComputerMove()
{
for
{
// First level of thought
CheckMove()
{
if (Move_Analyzed < Thinking_Depth)
{
Move_Analyzed++;
// Find the best possible human move-answer and continue the thinking tree
Find Best Human Move (HumanMove function);
Move_Analyzed++;
ComputerMove2()
{
for
{
CheckMove();
// Think deeper if you haven't reached the thinking depth
if (Move_Analyzed < Thinking_Depth)
[Think deeper, if necessary!];
// Record move analyzed as Best Move, if it has the best score
if (Move_Analyzed = Thinking_Depth)
CountScore();
[Record if best move];
}
// Return to the initial level of thinking to start
//analyzing a new thread
if (Move_Analyzed = Thinking_Depth)
Move_Analyzed = Move_Analyzed – 2;
}
}
}
}
V. Huo Chess Micro EditionI attempted to create a "Micro edition" of Huo Chess by stripping the program off every unnecessary line of code or file. In particular, in order to reduce the size, I used Huo Chess version 0.6 and did the following:
With the above steps implemented, the size was reduced from 53.5 KB to 47.5 KB. However, the code of the program can't be read at all! There is a price for small code... VI. Huo Chess Games ArchiveThat segment contains games played by Huo Chess versus other micro chess programs. GAME 1Date: 2007-11-11
GAME 2Date: 2007-11-11
GAME 3Date: 2008-01-22
GAME 4Date: 2008-02-22
Huo Chess (even without Opening Book) managed to play a very good game, during which it played well-structured chess. It didn't conduct any wrong moves, didn't give up any pieces and had a better position than Microchess at the end of the game (when the threefold repetition resulted in a draw). History
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||