An article about Huo Chess, a chess program in C++ and C# that attempts to be smaller in size than the Commodore-era Microchess

Please Sign up or sign in to vote.

News

2019-02-18: Huo Chess v0.991 Java edition was released. Refer to the main Huo Chess page at the Harmonia Philosophica portal for more on that.

2019-02-12: Huo Chess v0.991 is released. Many improvements in the algorithm. Added opening capability. The release includes also a Developers Edition with detailed logs of the thinking process.

2018-08-01: Huo Chess v0.990 development is completed. The new version deals with various issues in the code and features an updated logic in the chess engine.

2015-09-04: Huo Chess Windows 8 version was uploaded. The version has minimal user interaction capabilities. Newer versions will soon follow...

2015-09-04: Huo Chess v0.980 was updated: A hotfix was uploaded regarding CheckMove which had a bug due to which the computer made illegal moves. The version number has not changed since the hotfix was uploaded very close to the release of the new version. Thanks to Peter Bateman for pointing the bug out! (it's called regression testing and too often it is neglected...)

Summary

Huo Chess is an open source chess program of just 45 KB in size (this size refers to the 0.991 C# console application edition without GUI). The goal of the program is multifold: To achieve excellence in code structure and algorithm efficiency so as to create the smallest chess program which plays decent chess and (most importantly) to serve as an educational reference for people who want to learn how to develop a chess program.

Regarding the educational purpose of the program, the following points must be noted:

The source code is heavily commented so that the reader understands what he sees. The variables have full names (e.g. StartingColumn, ThinkingDepth, MovingPiece) and there are no shortcuts applied to obscure the code. Everything in the code is clean and nice so that it easy for everyone to understand the thinking process

so that the reader understands what he sees. The variables have full names (e.g. StartingColumn, ThinkingDepth, MovingPiece) and there are no shortcuts applied to obscure the code. Everything in the code is the thinking process I analyzed and documented the thinking process of the computer in this article so as to make it clear for everyone. Points for possible future improvements are also noted below so that everyone can contribute to future versions. An important contribution for a small-size "GUI" for the console application was submitted in the comments and is now a separate edition (included in the files distributed with reference to the guy who created it of course).

of the computer in this article so as to make it clear for everyone. Points for are also noted below so that everyone can contribute to future versions. An important contribution for a small-size "GUI" for the console application was submitted in the comments and is now a separate edition (included in the files distributed with reference to the guy who created it of course). The program contains a set of various logs which are easy to activate (simply uncomment the relative lines in the #Log or #WriteLog areas in the program). These logs generate text files which show how the chess engine algorithm works in detail. (provided that you have the time and patience to read them, they are truly long and detailed)

which are easy to activate (simply uncomment the relative lines in the #Log or #WriteLog areas in the program). These logs generate text files which show how the chess engine algorithm works in detail. (provided that you have the time and patience to read them, they are truly long and detailed) A series of "How to develop a chess program" has been created as part of the project. The series intends to show how to develop a chess program (as the title implies...) with step-by-step guidance for the inexperienced user. There is no point in trying to start from "Hello world" applications in order to learn programming! Go in the deep and in the time you will learn how to swim! (no worries, the worst thing that can happen is to have an application that does not compile). You can find the first lesson "How to develop a chess program for dummies" at Harmonia Philosophica portal. A very short tutorial on how to program a demo chess program in one day in Java can also be found here.

The program was developed with Microsoft Visual Studio 2017. (previous versions were created in Visual Studio 2013 and 2015). Four editions currently exist:

Huo Chess 0.991 with GUI Huo Chess 0.991 cs Huo Chess 0.991 cs with graphics Huo Chess 0.991 Developers version

Refer to the main Huo Chess page at the Harmonia Philosophica portal for the program's latest developments.

Version 0.991 highlights

The new version of Huo Chess (v0.991) is small in size (as all the previous ones) and much more capable. The official edition with Windows Forms user interface is 77 KB in size, the Console application with a chessboard User Interface (UI) edition is 48 KB and the Console application with no chessboard UI edition is 45 KB in size. In its current version (Huo Chess v0.990 – C#) can think up to 3 half-moves [Kakos-Minimax edition] (e.g. 2 half-moves for white and 1 half-move for black pieces when computer plays with White) and has an opening book capability.

Issues with the MinMax thinking algorithm are fixed and now the computer makes logical moves in every circumstance (at least for the depth of 3 half-moves in which it thinks). Capability of thinking at depth of 5 half-moves is added but not activated (must change Thinking_Depth to 4 for that to happen; note that thinking starts from move 0). Opening book capability has been added again (this capability existed but was removed in version 0.980) (must have positions in the "Opening Book" folder to use it).

Additional to the above, a Developers Edition has been created, which includes analytic logs of the thinking mechanism for debugging (for teaching purposes). The logs can be easily activated or deactivated. (Attention: Remember to check the size of the logs and delete them when needed! They can grow in size significantly) This edition also takes a small step towards showing the variant which the computer thinks to the user interface.

The Opening Book editor is distributed separately (see the list of Downloads). Along with the editor, a library of some sample opening book positions is also included in the distribution files.

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 is based on brute-force analysis while utilizing the MiniMax algorithm. It can be used to study the underlying logic of a chess program or as a basis for your own chess program. As I already mentioned, the source code is heavily commented in English and easily customizable, since all variables have distinct and understandable names. 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!

Introduction

What is most important when you program? Is it the program design? Yes? If "yes," then why is it 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 Accomplished

Driven by the above, I started developing a program in CLI C++ v8.0 (with Visual Studio 2008 Express Edition, while now the latest edition is developed in Visual Studio 2015/2017) to play chess with the intention of making a small in size (Micro)chess, even smaller than the Commodore-era Microchess. Now, one version in C# programming language and one version with Graphical User Interface also exist (you can find them here for downloading). 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 45 KB (in the C# console edition v0.991, which is a big improvement from the previous v0.961 version which was 55 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). I did not have the time to put Huo Chess v0.991 to play against Microchess again but I will some day...

How to Customize - Optimize the Program

The 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) with Huo Opening Book Editor.

You can also add more thinking depth capability, by adding new AnalyzeMove functions (like Analyze_Move_2_ComputerMove, Analyze_Move_4_ComputerMove, etc. for each new thinking depth layer) and make the necessary adjustments to the filters applied to the moves analyzed (these are needed to increase the speed of the program – see FindAttackers / FindDefenders ).

You can also optimize the way Huo Chess thinks by changing the CountScore function and the way the computer (or its human opponent) values the pieces or the chessboard position. For example, if you change the score of the Queen in the CountScore function from 9 to 12 , then the HY will play aggressively to attack the opponent's queen and at the same time try harder to defend its own queen. You can also — for example — give a high scoring to the existence of an opening column with a rook controlling it, so as to make the computer play more with its rooks and try to take over columns with them.

Any FEEDBACK is WELCOME with better configurations of the Opening Book or the CountScore functions which are actually the heart of the system!

Current limitations of version 0.991 and thoughts for improvements

The current version (v0.991) is set to a thinking depth of 3 half-moves (this corresponds to Thinking_Depth = 2 in the code; remember that thinking starts with move 0). This is because the full blown MiniMax algorithm is very memory intensive and time consuming. You can set the thinking depth to 4 (an even number is required for the Thinking_Depth variable because the analysis of the computer is designed to "end" at the computer's turn) to make the computer think at a depth of 5 half-moves, but then the program will run much slower. I will optimize the code for that in a next version.

Note: In the previous version (v0.961) the program crashed Out of Memory when you set the ThinkingDepth variable to 4. This happened because of the size limits of the array which holded the scores of each node (i.e. of each possible position the computer analyzes). This has been resolved since version 0.971: I created different arrays for every level of thinking so that it is faster and overcome the array-size limitations. Another way out of this is to apply filters to the algorithm so that it does not spend time and resources analyzing every posible move [possible future improvement]. All these are thoughts for the next upcoming version...

Next versions

There is already a list of potential improvements for the next version of Huo Chess, which can be found below:

Reduce size by improving 'peripheral' functions (like CheckForBlackMate, ElegxosNomimotitas et cetera).

Increase efficiency of MiniMax algorithm, by taking into account the average score of each branch. (code already in place, but commented out)

Add neural network capability: This will be achieved at a very primitive level by having key points of the MiniMax algorithm (the inequalities at each node level analysis in ComputerMove) being dependent of values stored in external configuration files and by updating those files every time a game is played (increase 'score' if the game is a win, decrease if it resulted in defeat). This is still in preliminary stage, but I will try to have a relative Developer edition ready in the next version (without compromising the size).

Improve CountScore function, to take into account the position of the chessboard, whether you move the same piece twice in the opening et cetera.

An Huo Chess version in Java is also under way. You can find the Java version of Huo Chess v0.990 at the Harmonia Philosophica portal. This will be the basis for an Android port of the application.

I. Chess Algorithm Analysis (versions 0.971 - 0.991)

The program implements the MiniMax 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 ComputerMove function) and then tries all possible moves it can make. It analyzes these moves up to the thinking depth I have defined (via the ComputerMove -> Analyze_Move_1_HumanMove -> Analyze_Move_2_ComputerMove path), measures the score (see CountScore function) of the final position reached from all possible move variants and – finally – chooses the move that leads to the most promising (from a score point of view) position (ComputerMove function).

C# v0.990 Kakos-Minimax algorithm summary

A high-level summary of the progress is as follows:

ComputerMove : Scans the chessboard. Checks for dangerous squares. Makes all possible moves. (excluding moves which are "stupid" based on spefici criteria) CheckMove : Stores the initial values of the move and makes some additional checks (e.g. for check) If thinking depth not reached => call Analyze_Move_1_HumanMove Analyze_Move_1_HumanMove: Checks all possible answers of the human opponent If thinking depth not reached => call Analyze_Move_2_ComputerMove Analyze_Move_2_ComputerMove: Scans the chessboard and makes all possible moves for the computer at the next thinking level If thinking depth reached => record the score of the final position in the Nodes of the MiniMax algorithm The algorithm continues until all possible moves are scanned The MiniMax algorithm is finally used to calculate the best move via the analysis of the thinking tree Nodes

You can uncomment the log writting code (see #region WriteLog, #region NodesLogBEFORE and #region NodesLogAFTER) to see the progress of the computer thought while the computer thinks in the respective .txt files that will be created as the result of the thinking process.

A very useful Word document with the documentation of the Huo Chess thinking process is also distributed in the v0.990 full files pack (see at the top of this article).

Chess engine logic (v0.990)

A more detailed summary of the way v0.990 works can be seen below.

If human plays first => EnterMove

(else ComputerMove is called directly from Main_Console())

For the move entered by the human opponent…

Check legality of the move

Check for mate

Check if there is check active

Store move’s coordinates

Store the value of the piece human moves

Store the coordinates of where that piece moved [Human_last_move_target_rank/column]

ComputerMove [Move_Analyzed = 0]

#region InitializeNodes //Initialize all nodes #region StoreInitialPosition //Store initial position #region OpeningBookCheck //OPENING BOOK CHECK #region DangerousSquares //CHECK FOR DANGEROUS SQUARES // Initialize variables

For each possible move

{

// Check if the move is stupid

#region CheckStupidMove If Move < 5 and you move the knight to the edges => Stupid move etc… + Store moving piece’s value #endregion CheckStupidMove

If not stupid & Destination square not dangerous => ...

if ((ThisIsStupidMove.CompareTo("N") == 0) && (Skakiera_Dangerous_Squares[(m_FinishingColumnNumber - 1), (m_FinishingRank - 1)] == 0))...

Call CheckMove

CheckMove(Skakiera_Thinking, m_StartingRank, m_StartingColumnNumber, m_FinishingRank, m_FinishingColumnNumber, MovingPiece);

<Call CheckMove(Skakiera_Thinking)> to:

Check legality (Gr. Nomimotita) and validity (Gr. Orthotita) of the move

Check for mate (This is not done! Must add it!)

Check if there is check active

// If move analyzed in the first: Store move to ***_HY variables (so as to keep the initial move somewhere)

if (((m_OrthotitaKinisis == true) && (m_NomimotitaKinisis == true)) && (Move_Analyzed == 0)) => ...

If the move is valid...

if ((m_OrthotitaKinisis == true) && (m_NomimotitaKinisis == true))

// Do the move

ProsorinoKommati = Skakiera_Thinking[(m_FinishingColumnNumber - 1), (m_FinishingRank - 1)]; Skakiera_Thinking[(m_StartingColumnNumber - 1), (m_StartingRank - 1)] = ""; Skakiera_Thinking[(m_FinishingColumnNumber - 1), (m_FinishingRank - 1)] = MovingPiece;

Check the score after the computer move

if (Move_Analyzed == 0) then... NodeLevel_0_count++; Temp_Score_Move_0 = CountScore(Skakiera_Thinking, humanDangerParameter); if (Move_Analyzed == 2) then... NodeLevel_2_count++; Temp_Score_Move_2 = CountScore(Skakiera_Thinking, humanDangerParameter); // Store the best move score at this level if ((m_PlayerColor.CompareTo("Black") == 0) && (Temp_Score_Move_0 > bestScoreLevel0)) { bestScoreLevel0 = Temp_Score_Move_0; }

// Check if you can eat back the piece of the human which moved!

if ((m_FinishingColumnNumber == Human_last_move_target_column) && (m_FinishingRank == Human_last_move_target_row) && (ValueOfMovingPiece <= ValueOfHumanMovingPiece)) { Best_Move_StartingColumnNumber = m_StartingColumnNumber; Best_Move_StartingRank = m_StartingRank; Best_Move_FinishingColumnNumber = m_FinishingColumnNumber; Best_Move_FinishingRank = m_FinishingRank; possibility_to_eat_back = true; }

Check possibility to eat

If there is a possibility to eat a piece of the opponent, then there is no need to analyze any further...

If thinkin g depth not reached, call next level of analysis

if ((Move_Analyzed < Thinking_Depth) && (possibility_to_eat_back == false) && (possibility_to_eat == false)) { Move_Analyzed = Move_Analyzed + 1; for (i = 0; i <= 7; i++) { for (j = 0; j <= 7; j++) { Skakiera_Move_After[(i), (j)] = Skakiera_Thinking[(i), (j)]; } } Who_Is_Analyzed = "Human"; Analyze_Move_1_HumanMove(Skakiera_Move_After); }

// Undo the move

Skakiera_Thinking[(m_StartingColumnNumber0 - 1), (m_StartingRank0 - 1)] = MovingPiece0; Skakiera_Thinking[(m_FinishingColumnNumber0 - 1), (m_FinishingRank0 - 1)] = ProsorinoKommati0;

}

...

}

Analyze_Move_1_HumanMove [Move_Analyzed = 1]

Analyze all possible moves

{

// If the move is valid and legal...

if ((m_OrthotitaKinisis == true) && (m_NomimotitaKinisis == true)) then... // Do the move ProsorinoKommati = Skakiera_Human_Thinking_2[(m_FinishingColumnNumber - 1), (m_FinishingRank - 1)]; Skakiera_Human_Thinking_2[(m_StartingColumnNumber - 1), (m_StartingRank - 1)] = ""; Skakiera_Human_Thinking_2[(m_FinishingColumnNumber - 1), (m_FinishingRank - 1)] = MovingPiece;

// Measure score AFTER the move

NodeLevel_1_count++; Temp_Score_Move_1_human = CountScore(Skakiera_Human_Thinking_2, humanDangerParameter);

// Store the best move at this level

if ((m_PlayerColor.CompareTo("Black") == 0) && (Temp_Score_Move_1_human < bestScoreLevel1)) { bestScoreLevel1 = Temp_Score_Move_1_human; }

If thinking depth not reached, call next level of analysis

if (Move_Analyzed < Thinking_Depth) && (Temp_Score_Move_1_human better than bestScoreLevel1) { Move_Analyzed = Move_Analyzed + 1; Who_Is_Analyzed = "HY"; if (Move_Analyzed == 2) Analyze_Move_2_ComputerMove(Skakiera_Move_After); else if (Move_Analyzed == 4) Analyze_Move_4_ComputerMove(Skakiera_Move_After); else if (Move_Analyzed == 6) Analyze_Move_6_ComputerMove(Skakiera_Move_After); }

// Undo the move

Skakiera_Human_Thinking_2[(m_StartingColumnNumber1 - 1), (m_StartingRank1 - 1)] = MovingPiece1; Skakiera_Human_Thinking_2[(m_FinishingColumnNumber1 - 1), (m_FinishingRank1 - 1)] = ProsorinoKommati1;

}

// Return to previous depth of analysis

Move_Analyzed = Move_Analyzed - 1; Who_Is_Analyzed = "HY";

Analyze_Move_2_ComputerMove [Move_Analyzed = 2]

Check all possible moves

{

// If the move is valid and legal...

if ((m_OrthotitaKinisis == true) && (m_NomimotitaKinisis == true))

{

// Do the move

ProsorinoKommati = Skakiera_Thinking_HY_2[(m_FinishingColumnNumber - 1), (m_FinishingRank - 1)]; Skakiera_Thinking_HY_2[(m_StartingColumnNumber - 1), (m_StartingRank - 1)] = ""; Skakiera_Thinking_HY_2[(m_FinishingColumnNumber - 1), (m_FinishingRank - 1)] = MovingPiece;

// Check the score after the computer move.

NodeLevel_2_count++; Temp_Score_Move_2 = CountScore(Skakiera_Thinking_HY_2, humanDangerParameter);

// Store the best score at this level

if ((m_PlayerColor.CompareTo("Black") == 0) && (Temp_Score_Move_2 > bestScoreLevel2)) { bestScoreLevel2 = Temp_Score_Move_2; }

If thinking depth not reached, call next level of analysis...

if (Move_Analyzed < Thinking_Depth) { Move_Analyzed = Move_Analyzed + 1; Who_Is_Analyzed = "Human"; // Check human move if (Move_Analyzed == 1) Analyze_Move_1_HumanMove(Skakiera_Move_After); else if (Move_Analyzed == 3) Analyze_Move_3_HumanMove(Skakiera_Move_After); else if (Move_Analyzed == 5) Analyze_Move_5_HumanMove(Skakiera_Move_After); }

// If you have reached the defined depth of analysis...

if (Move_Analyzed == Thinking_Depth) { // [MiniMax algorithm - skakos] // Record the node in the Nodes Analysis array (to use with MiniMax algorithm) skakos NodesAnalysis0[NodeLevel_0_count, 0] = Temp_Score_Move_0; NodesAnalysis1[NodeLevel_1_count, 0] = Temp_Score_Move_1_human; NodesAnalysis2[NodeLevel_2_count, 0] = Temp_Score_Move_2; NodesAnalysis3[NodeLevel_3_count, 0] = Temp_Score_Move_3_human; // Store the parents (number of the node of the upper level) NodesAnalysis0[NodeLevel_0_count, 1] = 0; NodesAnalysis1[NodeLevel_1_count, 1] = NodeLevel_0_count; NodesAnalysis2[NodeLevel_2_count, 1] = NodeLevel_1_count; NodesAnalysis3[NodeLevel_3_count, 1] = NodeLevel_2_count; }

Note: Because the analysis ends only in ComputerMove functions, the ThinkigDepth must be an even number!

// Undo the move

Skakiera_Thinking_HY_2[(m_StartingColumnNumber2 - 1), (m_StartingRank2 - 1)] = MovingPiece2;

Skakiera_Thinking_HY_2[(m_FinishingColumnNumber2 - 1), (m_FinishingRank2 - 1)] = ProsorinoKommati2;

}

Move_Analyzed = Move_Analyzed - 1; Who_Is_Analyzed = "Human";

ComputerMove [Continued... - The End of Analysis]

Check for mate

// Find the Best Move: Use MiniMax algorithm (only if possibility to eat a piece of the opponent is false)

if (possibility_to_eat_back == false) { // [MiniMax algorithm - skakos] // Find node 1 move with the best score via the MiniMax algorithm. int counter0, counter1, counter2; // ------------------------------------------------------ // NodesAnalysis // ------------------------------------------------------ // Nodes structure... // [ccc, xxx, 0]: Score of node No. ccc at level xxx // [ccc, xxx, 1]: Parent of node No. ccc at level xxx-1 // ------------------------------------------------------ int parentNodeAnalyzed = -999; //v0.980: Remove //parentNodeAnalyzed = -999; for (counter2 = 1; counter2 <= NodeLevel_2_count; counter2++) { if (Int32.Parse(NodesAnalysis2[counter2, 1].ToString()) != parentNodeAnalyzed) { //parentNodeAnalyzedchanged = true; parentNodeAnalyzed = Int32.Parse(NodesAnalysis2[counter2, 1].ToString()); NodesAnalysis1[Int32.Parse(NodesAnalysis2[counter2, 1].ToString()), 0] = NodesAnalysis2[counter2, 0]; } if (NodesAnalysis2[counter2, 0] >= NodesAnalysis1[Int32.Parse(NodesAnalysis2[counter2, 1].ToString()), 0]) NodesAnalysis1[Int32.Parse(NodesAnalysis2[counter2, 1].ToString()), 0] = NodesAnalysis2[counter2, 0]; } // Now the Node1 level is filled with the score data parentNodeAnalyzed = -999; for (counter1 = 1; counter1 <= NodeLevel_1_count; counter1++) { if (Int32.Parse(NodesAnalysis1[counter1, 1].ToString()) != parentNodeAnalyzed) { //parentNodeAnalyzedchanged = true; parentNodeAnalyzed = Int32.Parse(NodesAnalysis1[counter1, 1].ToString()); NodesAnalysis0[Int32.Parse(NodesAnalysis1[counter1, 1].ToString()), 0] = NodesAnalysis1[counter1, 0]; } if (NodesAnalysis1[counter1, 0] <= NodesAnalysis0[Int32.Parse(NodesAnalysis1[counter1, 1].ToString()), 0]) NodesAnalysis0[Int32.Parse(NodesAnalysis1[counter1, 1].ToString()), 0] = NodesAnalysis1[counter1, 0]; } // Choose the biggest score at the Node0 level // Check example at http://en.wikipedia.org/wiki/Minimax#Example_2 // Initialize the score with the first score and move found double temp_score = NodesAnalysis0[1, 0]; Best_Move_StartingColumnNumber = Int32.Parse(NodesAnalysis0[1, 2].ToString()); Best_Move_StartingRank = Int32.Parse(NodesAnalysis0[1, 4].ToString()); Best_Move_FinishingColumnNumber = Int32.Parse(NodesAnalysis0[1, 3].ToString()); Best_Move_FinishingRank = Int32.Parse(NodesAnalysis0[1, 5].ToString()); for (counter0 = 1; counter0 <= NodeLevel_0_count; counter0++) { if (NodesAnalysis0[counter0, 0] > temp_score) { temp_score = NodesAnalysis0[counter0, 0]; Best_Move_StartingColumnNumber = Int32.Parse(NodesAnalysis0[counter0, 2].ToString()); Best_Move_StartingRank = Int32.Parse(NodesAnalysis0[counter0, 4].ToString()); Best_Move_FinishingColumnNumber = Int32.Parse(NodesAnalysis0[counter0, 3].ToString()); Best_Move_FinishingRank = Int32.Parse(NodesAnalysis0[counter0, 5].ToString()); } } }

Do the move

Redraw the chessboard

If no move found => Resign

II. Huo Chess Thought Flow [Minimax algorithm] (versions 0.971, 0.980)

Below, 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() function. Be aware that some code segments may be slightly different from the code in the distributed ZIP file since I continuously change the program. As it appears, the "constant beta" state is the trend nowadays.

ComputerMove (Start)

Initialize nodes

Store initial position

Check opening book

Check dangerous squares

Analyze all possible moves

for (...) { MovingPiece = Skakiera_Thinking[(iii), (jjj)]; m_StartingColumnNumber = iii + 1 ; m_FinishingColumnNumber = w + 1 ; m_StartingRank = jjj + 1 ; m_FinishingRank = r + 1 ; MovingPiece0 = MovingPiece; m_StartingColumnNumber0 = m_StartingColumnNumber; m_FinishingColumnNumber0 = m_FinishingColumnNumber; m_StartingRank0 = m_StartingRank; m_FinishingRank0 = m_FinishingRank; ProsorinoKommati0 = Skakiera_Thinking[(m_Finishi…)]; } (check all possible moves)

Call CheckMove(Skakiera_Thinking) to perform some checks

CheckMove

Store initial values of the move

Check for mate

Check for check

Check legality and validity of the move (only for ComputerMove!)

Computer move (back from CheckMove)

For each possible move

Count score

If the move under analysis is correct and legal, do it and measure its score

if ((m_OrthotitaKinisis == true ) && (m_NomimotitaKinisis == true )) { DO THE MOVE if (Move_Analyzed == 0 ) { NodeLevel_0_count++; Temp_Score_Move_0 = CountScore(Skakie…); } if (Move_Analyzed == 2 ) { NodeLevel_2_count++; Temp_Score_Move_2 = CountScore(Skakie…); } if (Move_Analyzed == 4 ) { NodeLevel_4_count++; Temp_Score_Move_4 = CountScore(Skakie…); } if (Move_Analyzed == 6 ) { NodeLevel_6_count++; Temp_Score_Move_6 = CountScore(Skakie…); }

If you have not reached the thinking depth, call the next level analysis functions...

Increase nodes count

Store the score at that node

Call next function to search at a deeper level...

if (Move_Analyzed < Thinking_Depth) { Move_Analyzed = Move_Analyzed + 1 ; Who_Is_Analyzed = " Human" ; if (Move_Analyzed == 1 ) Analyze_Move_1_HumanMove(Skakiera_Move_After); else if (Move_Analyzed == 3 ) Analyze_Move_3_HumanMove(Skakiera_Move_After); else if (Move_Analyzed == 5 ) Analyze_Move_5_HumanMove(Skakiera_Move_After); }

Analyze_Move_1_HumanMove (called from ComputerMove)

for ... (check all possible moves) { MovingPiece1 = MovingPiece; m_StartingColumnNumber1 = m_StartingColumnNumber; m_FinishingColumnNumber1 = m_FinishingColumnNumber; m_StartingRank1 = m_StartingRank; m_FinishingRank1 = m_FinishingRank; ProsorinoKommati1 = Skakiera_Human_Thinking_2[(m_Fi...)];

If the move is legal and valid...

if ((m_OrthotitaKinisis == true ) && (m_NomimotitaKinisis == true ))

Do the move

Measure score AFTER the move

if (Move_Analyzed == 1 ) { NodeLevel_1_count++; Temp_Score_Move_1_human = CountScore(Skakiera_Human_Thinking_2, humanDangerParameter); } if (Move_Analyzed == 3 ) { NodeLevel_3_count++; Temp_Score_Move_3_human = CountScore(Skakiera_Human_Thinking_2, humanDangerParameter); } if (Move_Analyzed == 5 ) { NodeLevel_5_count++; Temp_Score_Move_5_human = CountScore(Skakiera_Human_Thinking_2, humanDangerParameter); }

If thinking depth not reached, call the next level thinking function...

if (Move_Analyzed < Thinking_Depth) { 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_2[(i), (j)]; } } if (Move_Analyzed == 2 ) Analyze_Move_2_ComputerMove(Skakiera_Move_After); else if (Move_Analyzed == 4 ) Analyze_Move_4_ComputerMove(Skakiera_Move_After); else if (Move_Analyzed == 6 ) Analyze_Move_6_ComputerMove(Skakiera_Move_After); }

Analyze_Move_2_ComputerMove (called from Analyze_Move_1_HumanMove)

Analyze all possible moves

for (...) { MovingPiece = Skakiera_Thinking_template[(iii), (jjj)]; m_StartingColumnNumber = iii + 1 ; m_FinishingColumnNumber = w + 1 ; m_StartingRank = jjj + 1 ; m_FinishingRank = r + 1 ; }

Check the move

Is it legal?

Is it valid?

If the move is valid and legal...

if ((m_OrthotitaKinisis == true ) && (m_NomimotitaKinisis == true ))

Do the move

Check the score after the computer move.

if (Move_Analyzed == 0 ) { NodeLevel_0_count++; Temp_Score_Move_0 = CountScore(Skakiera_Thinking_HY_2, humanDangerParameter); } if (Move_Analyzed == 2 ) { NodeLevel_2_count++; Temp_Score_Move_2 = CountScore(Skakiera_Thinking_HY_2, humanDangerParameter); } if (Move_Analyzed == 4 ) { NodeLevel_4_count++; Temp_Score_Move_4 = CountScore(Skakiera_Thinking_HY_2, humanDangerParameter); } if (Move_Analyzed == 6 ) { NodeLevel_6_count++; Temp_Score_Move_6 = CountScore(Skakiera_Thinking_HY_2, humanDangerParameter); }

If thinking depth is reached, record the nodes in the Nodes Analysis array (to use with MiniMax algorithm)

if (Move_Analyzed == Thinking_Depth) { NodesAnalysis[NodeLevel_0_count, 0 , 0 ] = Temp_Score_Move_0; NodesAnalysis[NodeLevel_1_count, 1 , 0 ] = Temp_Score_Move_1_human; NodesAnalysis[NodeLevel_2_count, 2 , 0 ] = Temp_Score_Move_2; NodesAnalysis[NodeLevel_3_count, 3 , 0 ] = Temp_Score_Move_3_human; NodesAnalysis[NodeLevel_4_count, 4 , 0 ] = Temp_Score_Move_4; NodesAnalysis[NodeLevel_5_count, 5 , 0 ] = Temp_Score_Move_5_human; NodesAnalysis[NodeLevel_6_count, 6 , 0 ] = Temp_Score_Move_6; NodesAnalysis[NodeLevel_0_count, 0 , 1 ] = 0 ; NodesAnalysis[NodeLevel_1_count, 1 , 1 ] = NodeLevel_0_count; NodesAnalysis[NodeLevel_2_count, 2 , 1 ] = NodeLevel_1_count; NodesAnalysis[NodeLevel_3_count, 3 , 1 ] = NodeLevel_2_count; NodesAnalysis[NodeLevel_4_count, 4 , 1 ] = NodeLevel_3_count; NodesAnalysis[NodeLevel_5_count, 5 , 1 ] = NodeLevel_4_count; NodesAnalysis[NodeLevel_6_count, 6 , 1 ] = NodeLevel_5_count; }

If thinking depth is not reached, call the next level and so on...

if (Move_Analyzed < Thinking_Depth) { Move_Analyzed = Move_Analyzed + 1 ; for (i = 0 ; i < = 7 ; i++) { for (j = 0 ; j < = 7 ; j++) { Skakiera_Move_After[(i), (j)] = Skakiera_Thinking[(i), (j)]; } } Who_Is_Analyzed = " Human" ; First_Call_Human_Thought = true ; if (Move_Analyzed == 1 ) Analyze_Move_1_HumanMove(Skakiera_Move_After); else if (Move_Analyzed == 3 ) Analyze_Move_3_HumanMove(Skakiera_Move_After); else if (Move_Analyzed == 5 ) Analyze_Move_5_HumanMove(Skakiera_Move_After); }

Undo the move

Return to previous level...

Move_Analyzed = Move_Analyzed - 1 ; Who_Is_Analyzed = " Human" ;

Analyze_Move_1_HumanMove (back from Analyze_Move_2_ComputerMove)

Undo the move

Return to previous level...

Move_Analyzed = Move_Analyzed - 1 ; Who_Is_Analyzed = " HY" ;

ComputerMove (back from Analyze_Move_1_HumanMove)

Undo the move

Skakiera_Thinking[(m_StartingColumnNumber0 - 1 ), (m_StartingRank0 - 1 )] = MovingPiece0; Skakiera_Thinking[(m_FinishingColumnNumber0 - 1 ), (m_FinishingRank0 - 1 )] = ProsorinoKommati0; } (end of for loop)

Find if there is mate

DO THE BEST MOVE FOUND!

[MiniMax algorithm – skakos - START]

Check Minimax algorithm http://en.wikipedia.org/wiki/Minimax for how the algorithm works!

prntNodeAnalyzed = -999; for (counter6 = 1 ; counter6 < = NodeLevel_6_count; counter6++) { if ( Int32 .Parse(NodesAnalysis[counter6, 6 , 1 ].ToString()) != prntNodeAnalyzed) { prntNodeAnalyzed = Int32 .Parse(NodesAnalysis[counter6, 6 , 1 ].ToString()); NodesAnalysis[ Int32 .Parse(NodesAnalysis[counter6, 6 , 1 ].ToString()), 5 , 0 ] = NodesAnalysis[counter6, 6 , 0 ]; } if (NodesAnalysis[counter6, 6 , 0 ] < = NodesAnalysis[ Int32 .Parse(NodesAnalysis[counter6, 6 , 1 ].ToString()), 5 , 0 ]) NodesAnalysis[ Int32 .Parse(NodesAnalysis[counter6, 6 , 1 ].ToString()), 5 , 0 ] = NodesAnalysis[counter6, 6 , 0 ]; } prntNodeAnalyzed = -999; for (counter5 = 1 ; counter5 < = NodeLevel_5_count; counter5++) { if ( Int32 .Parse(NodesAnalysis[counter5, 5 , 1 ].ToString()) != prntNodeAnalyzed) { prtNodeAnalyzed = Int32 .Parse(NodesAnalysis[counter5, 5 , 1 ].ToString()); NodesAnalysis[ Int32 .Parse(NodesAnalysis[counter5, 5 , 1 ].ToString()), 4 , 0 ] = NodesAnalysis[counter5, 5 , 0 ]; } if (NodesAnalysis[counter5, 5 , 0 ] > = NodesAnalysis[ Int32 .Parse(NodesAnalysis[counter5, 5 , 1 ].ToString()), 4 , 0 ]) NodesAnalysis[ Int32 .Parse(NodesAnalysis[counter5, 5 , 1 ].ToString()), 4 , 0 ] = NodesAnalysis[counter5, 5 , 0 ]; } ...

[MiniMax algorithm – skakos - END]

REDRAW THE CHESSBOARD

if (m_PlayerColor.CompareTo( " Black" ) == 0 ) m_WhichColorPlays = " Black" ; else if (m_PlayerColor.CompareTo( " White" ) == 0 ) m_WhichColorPlays = " White" ; m_WhoPlays = " Human" ;

[CountScore]

Every move score is measured (if the move is legal and correct). These scores are stored in the NodesAnalysis array (see below). The scoring function is the heart of the program. It currently takes into account mainly material values, with some positional considerations for the opening phase of the game (i.e. if Move < 11 it is not good to move your Queen or else a small “scoring penalty” is imposed). The optimization of that function is key to the increasing of the computer play strength.

MiniMax algorithm applied

When we have reached the thinking depth (i.e. when we have reached the ComputerMove function which we have defined as the last one in the chain of analysis), we store the chessboard scores of the thinking tree nodes for every thinking depth level (applies for version 0.93 and newer).

These nodes are then going to be used in the MiniMax algorithm to find the best move.

NodesAnalysis[NodeLevel_1_count, 1 , 0 ] = Temp_Score_Human_before_2; NodesAnalysis[NodeLevel_2_count, 2 , 0 ] = Temp_Score_Human_after_2; NodesAnalysis[NodeLevel_3_count, 3 , 0 ] = Temp_Score_Human_before_4; NodesAnalysis[NodeLevel_4_count, 4 , 0 ] = Temp_Score_Human_after_4; NodesAnalysis[NodeLevel_5_count, 5 , 0 ] = Temp_Score_Human_before_6; NodesAnalysis[NodeLevel_6_count, 6 , 0 ] = Temp_Score_Human_after_6;

For every node, we also store the number of the parent node:

NodesAnalysis[NodeLevel_1_count, 1 , 1 ] = 0 ; NodesAnalysis[NodeLevel_2_count, 2 , 1 ] = NodeLevel_1_count; NodesAnalysis[NodeLevel_3_count, 3 , 1 ] = NodeLevel_2_count; NodesAnalysis[NodeLevel_4_count, 4 , 1 ] = NodeLevel_3_count; NodesAnalysis[NodeLevel_5_count, 5 , 1 ] = NodeLevel_4_count;

This is required for the MiniMax algorithm implementation (see http://en.wikipedia.org/wiki/Minimax on how this algorithm works): We start from the lower level nodes and go up to the beginning of the tree, like in the schema that follows:

Suppose the game being played only has a maximum of two possible moves per player each turn. The algorithm generates the tree shown in the figure above, where the circles represent the moves of the computer AI running the algorithm (maximizing player), and squares represent the moves of the human opponent (minimizing player). For the example’s needs, the tree is limited to a look-ahead of 4 moves.

The algorithm evaluates each leaf node using the CountScore evaluation functions, obtaining the values shown. The moves where the maximizing player wins are assigned with positive infinity, while the moves that lead to a win of the minimizing player are assigned with negative infinity (this is again for illustration purposes only – infinity will not happen in the game as it is currently developed). At level 3, the algorithm will choose, for each node, the smallest of the child node values, and assign it to that same node (e.g. the node on the left will choose the minimum between "10" and "+8", therefore assigning the value "10" to itself). The next step, in level 2, consists of choosing for each node the largest of the child node values. Once again, the values are assigned to each parent node. The algorithm continues evaluating the maximum and minimum values of the child nodes alternately until it reaches the root node, where it chooses the move with the largest value (represented in the figure with a blue arrow). This is the move that the player should make in order to minimize the maximum possible loss.

In order for the program to calculate the best move, a number of “ for loops” are applied so as to make the abovementioned backwards computation possible.

for (counter7 = 1 ; counter7 <= NodeLevel_7_count; counter7++) { for (counter8 = 1 ; counter8 <= NodeLevel_8_count; counter8++) { if (NodesAnalysis[counter8, 8 , 1 ] == counter7) { if (counter8 == 1 ) NodesAnalysis[counter7, 7 , 0 ] = NodesAnalysis[counter8, 8 , 0 ]; if (counter8 > 1 ) if (NodesAnalysis[counter8, 8 , 0 ] < NodesAnalysis[counter7, 7 , 0 ]) NodesAnalysis[counter7, 7 , 0 ] = NodesAnalysis[counter8, 8 , 0 ]; } } }

After the algorithm has reached the root node, the move with the best score is selected.

ComputerMove [Maximum thinking depth] – End

III. Huo Chess Thinking Flow (v0.95 Simple-Minimax)

ComputerMove() { DangerousSquares MoveFilter for all possible moves { Temporarily make all possible moves Count the score of these moves Call ComputerMove2 (with the temporary chessboard as input) ComputerMove2() { ... ComputerMove5() { Record the moves and their scores in the Nodes Analysis array } } } Do the best move (MiniMax); }

IV. Huo Chess Thought Flow (v0.93 Kakos-Minimax or all v0.84 and older versions)

In this section, I analyze the thinking algorithm for version 0.93-Kakos-Minimax or for versions 0.84 and older. (the main difference between these are the method used to find the best move after the analysis of all possible moves is complete) Below, 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

Computer Level: Maitre (ThinkingDepth = 2)

ComputerMove - Start

Step 1

START

Move_Analyzed = 0

1. If the first time called -> store initial chessboard position. 2. if( Move_Analyzed >Thinking_Depth ) 3. Stop_Analyzing = true; 4. if( Stop_Analyzing = false) 5. Scan chessboard. for iii, jjj 6. Scan chessboard, find a piece of the HY , conduct move, check correctness and legality of move, and if all is OK, then call CheckMove to measure the score of the move.

Call: CheckMove

CheckMove - Start

Number of moves analyzed ++. Check correctness and legality of move. Check if there is a mate on the chessboard. If the move is correct and legal, then do it. Check if there is a pawn to be promoted. Store move to ***_HY variables because, after many calls of ComputerMove and CheckMove functions, the initial values of the move analyzed will be lost. If this is the first move analyzed, then record it as the correct "best" move, no matter how bad it is.

Step 2

IF result: FALSE Move_Analyzed = 0

if( Move_Analyzed = Thinking_Depth ) Measure the score of the move and record it as "best" move if it is larger than the score of the so-far best move score.

Step 3

IF result: TRUE Move_Analyzed = 1

if ( Move_Analyzed < Thinking_Depth )

HumanMove - Start [ HumanMove_Template for v0.93]

Step 4

Find the best answer of the Human (Move_Analyzed = 1).

Version 0.93: Find ALL possible human moves

Scan the chessboard -> find any possible move.

Call CheckHumanMove . [redundant in v0.93]

Store the chessboard score before and after the human move.

CheckHumanMove - Start

voidCheckHumanMove(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.

In v0.93 and newer: Record the score before and after the human opponents makes his move.

Those scores are recorded in the Nodes Analysis array and will be used for the MiniMax algorithm at the end (to evaluate the best move).

CheckHumanMove - End

Conduct the best move of the human [conduct all possible human moves in v0.93].

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

Move_Analyzed = 2

Step 6

CALL next ComputerMove function for next-level move analysis.

Move_Analyzed = 2 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 7

Scan the chessboard and find the best move for the computer.

Move_Analyzed = 2 voidComputerMove2(array<STRING^, />^ Skakiera_Thinking_2) { // Same as…ComputerMove if(Move_Analyzed has not reached thinking depth) { // Same as…ComputerMove: Call CheckMove -> HumanMove -> next ComputerMove etc // (If we haven't reached the desired level of analysis, then the HumanMove // will be called again, then again the ComputerMove function etc.) }

Step 8

Return to a previous ComputerMove (i.e. ComputerMove4 calls ComputerMove2 ) function to continue the analysis.

Move_Analyzed = 2 (at the end of the analysis this variable will be equal to 0, see Step 9).

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 - End

HumanMove - End

CheckMove - End

If no legal move is found -> we have MATE!

Step 9

Version 0.93: Apply the MiniMax algorithm to reach to the best move.

Play the move with the highest score. Now it is the Human's turn to play.

if (move_analyzed== 0 ){

Check if it is good to conduct castling. "Redraw" the chessboard with the best move found. Move the rook next to the king, if castling occurred. Check if a pawn is promoted (at the current version computer always promotes a pawn to queen). Now it is the turn of the other player (human) to play!

} 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 – End

Thinking Flow summary for v0.93 Kakos-Minimax or v0.84 and older versions

ComputerMove() { for { CheckMove() { if (Move_Analyzed <Thinking_Depth) { Move_Analyzed++; Find Best Human Move (HumanMove function) / Find all possible human moves (v0. 93 ); Record chessboard scores before and after the human move; Move_Analyzed++; Go to next thinking depth ComputerMove2() { for { CheckMove(); if (Move_Analyzed <Thinking_Depth) [Think deeper, if necessary!]; if (Move_Analyzed = Thinking_Depth) CountScore(); [Record if best move]; } Move_Analyzed = Move_Analyzed – 2 ; } } } Do the best move; }

V. Huo Chess GUI Edition

The Graphical User interface (GUI) edition of Huo Chess consists of a simple chessboard which shows the current chessboard state. It does support mouse move so you do not have to enter the moves via keyboard. More advanced GUI editions with come in the future. It must be noted that the GUI did not affect the total size of the program much. The Huo Chess C# 0.980 GUI edition has a total size of 78 KB. Future plans include the development of a PGN compatible program which can utilize any Chess GUI available on the Internet.

VI. Huo Chess Games Archive

That segment contains games played by Huo Chess versus other micro chess programs.

GAME 1

Date: 2007-11-11

Place: Athens, Greece

White: HuoChess v0.4 (with Opening Book) [as distributed by The Code Project]

Black: Microchess (as provided by BenLo Park)

Result: Draw by threefold repetition

1. d4 Nc6 2. d5 Nb4 3. Nc3 e5 4. Bg5 Qxg5 5. Nh3 Qg4 6. e4 Qh4 7. Be2 d6 8. Bb5+ Kd8 9. Nf4 Qxf4 10. h3 Nf6 11. f3 Qe3+ 12. Be2 Bd7 13. f4 Qg3+ 14. Kd2 Qxf4+ 15. Ke1 Qg3+ 16. Kd2 Qf4+ 17. Ke1 Qg3+ 18. Kd2 Qf4+ 19. Ke1 Qg3+ 20. Kd2 Qf4+ 21. Ke1 [draw by threefold repetition]

GAME 2

Date: 2007-11-11

Place: Athens, Greece

White: Microchess (as provided by BenLo Park)

Black: HuoChess v0.4 (with Opening Book) [as distributed by The Code Project]

Result: Draw by threefold repetition

1. e4 e6 2. Qh5 d6 3. Bb5+ Ke7 4. Qg5+ f6 5. Qh5 h6 6. d4 g6 7. Qxg6 Nd7 8. Bf4 f5 9. exf5 Ndf6 10. fxe6 Bxe6 11. a4 Bg7 12. Qxg7+ Bf7 13. Qxh8 Bh5 14. Qg7+ Bf7 15. Nc3 h5 16. Nf3 Nh7 17. Nd5+ Ke6 18. c4 c6 19. Nc7+ Qxc7 20. d5+ cxd5 21. Nd4+ Ke7 22. Nf5+ Ke6 23. Nd4+ Ke7 24. Nf5+ Ke6 25. Nd4+ Ke7 26. Nf5+ Ke6 27. Nd4+ Ke7 28. Nf5+ [draw by threefold repetition]

GAME 3

Date: 2008-01-22

Place: Athens, Greece

White: Microchess (as provided by BenLo Park)

Black: HuoChess v0.5 (with Opening Book) [as distributed by The Code Project]

Result: Draw by threefold repetition

1. e2-e4 e7-e6 2. d1-h5 c7-c5 3. f1-b5 g8-f6 4. h5-e5 f6-g4 5. e5-f4 g4xf2 6. e1xf2 g7-g5 7. f4-e5 a7-a6 8. b5-c4 f7-f6 9. e5-g3 d7-d6 10. g1-h3 h7-h6 11. h1-e1 e8-e7 12. b1-a3 d8-b6 13. d2-d4 b6-a5 14. c1-d2 a5xd2+ 15. e1-e2 d2xd4+ 16. 16.f2-f3 d4xb2 17. a1-d1 b2xa3+ 18. c2-c3 a3xc3+ 19. c4-d3 a6-a5 20. f3-g4 e7-d7 21. d3-b5+ d7-e7 22. g3xc3 f8-g7 23. e2-f2 e7-f7 24. d1xd6 a8-a7 25. c3xc5 b8-c6 26. f2-f3 b7-b6 27. c5xb6 c8-d7 28. f3-c3 e6-e5+ 29. d6xd7+ a7xd7 30. b5-c4+ f7-e7 31. b6-c5+ d7-d6 32. c3-d3 c6-d4 33. c5-c7+ d6-d7 34. c7-c5+ d7-d6 35. c5-c7+ d6-d7 36. c7-c5+ d7-d6 [draw by threefold repetition]

GAME 4

Date: 2008-02-22

Place: Athens, Greece

White: Microchess (as provided by BenLo Park)

Black: HuoChess v0.6 (without Opening Book) [as distributed by The Code Project]

Result: Draw by threefold repetition

1. Nf3 d5 2. Nc3 Qd6 3. Na4 Qf4 4. c3 Bd7 5. d4 Qe4 6. Ng5 Qg4 7. f3 Qh4+ 8. g3 Qh5 9. Nc5 Bb5 10. b3 f6 11. Nge6 b6 12. e3 Bc6 13. Nd3 Bd7 14. Nxf8 Kxf8 15. Nf4 Qg5 16. Ne2 Nc6 17. f4 Qg4 18. h3 Qf3 19. Rh2 Nh6 20. Kd2 Nf5 21. Kc2 Qe4+ 22. Kb2 Qf3 23. Kc2 Qe4+ 24. Kb2 Qf3 25. Kc2 Qe4+

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).

GAME 5

Date: 2009-01-25

Place: Athens, Greece

White: Microchess (as provided by BenLo Park)

Black: HuoChess v0.82 (with Opening Book) [as distributed by The Code Project]

Result: Draw by threefold repetition

1. e4 e6 2. Qh5 Qe7 3. Bc4 Kd8 4. d4 a6 5. Bf4 d5 6. exd5 f5 7. dxe6 Bxe6 8. Bxe6 g6 9. Qe2 Nd7 10. Bxc7+ Kxc7 11. Qc4+ Nc5 12. dxc5 Qg7 13. Qf4+ Kc6 14. Qf3+ Kxc5 15. Qd5+ Kb6 16. Qb3+ Kc7 17. Qc4+ Kd6 18. Qd5+ Kc7 19. Qc4+ Kd6 20. Qd5+ Kc7 21. Qc4+ Kd6 22. Qd5+ draw by threefold repetition

Huo Chess played a good game. It did not give up pieces without reason and did not lose chances to kill opponent’s pieces when possible.

History

3 October, 2007 -- Original version posted

15 October, 2007 -- Version 0.2

22 October, 2007 -- Version 0.3

15 November, 2007 -- Version 0.4

25 January, 2008 -- Version 0.5

28 January, 2008 -- License info and download updated

28 February, 2008 -- Article content and downloads updated

3 July, 2008 -- Version 0.721 - Article content and downloads updated

8 January, 2009 -- Version 0.722 - Article content and downloads updated

22 April, 2009 - Version 0.82 - Article content and downloads updated

8 September, 2011 – Version 0.93 (C# only) – MiniMax algorithm update

19 September 2012 – Version 0.95 (C# only) - Update dangerous squares check & CountScore

12 August 2014 - Version 0.961 (C# only) - Fix problems with MiniMax algorithm

22 August 2015 - Version 0.971 (C# without GUI, with GUI, Micro edition) - Improve algorithm performance, fixed "Stupid move" filter et cetera

28 August 2015 - Version 0.980 (C# without GUI, with GUI, Micro edition) - Fixed bugs related to check and invalid human moves, added the "resign" functionality

2 August 2018 - Version 0.990 (C# without GUI, with GUI, Micro edition) - Updated engine, fixed bugs.

12 February 2019: Version 0.991 published (C#), including a Developers edition.

Versioning

Huo Chess is intelligently designed, but it also evolves. Currently, the program is at version 0.990. The timeline of versions and the respective changes conducted in the program's code are depicted below.

Version 0.991 (C#) Fixed some issues with the MinMax algorithm, which now performs as planned. Added again the opening book capability. [Visual Studio 2017] Version 0.990 (C#) Updated engine. Fixed bugs. [Visual Studio 2015/ 2017] Version 0.980 (C#) Fixed bugs related to check and invalid human moves, added the "resign" functionality [Visual Studio 2013/ 2015] Version 0.971 (C#) Fixed the Nodes Analysis: Created a different NodesAnalysis array for each level. Fixed bugs so that the analysis stores correctly the values and the MiniMax algorithm performs as it should. Fixed the “Stupid move” filter for the computer moves. Fixed the “Possibility to eat back” functionality. [Visual Studio 2013/ 2015] Version 0.961 (C#) Changes in Huo Chess 0.961: Fixed problems and bugs related to the MiniMax algorithm. [Visual Studio 2012] Version 0.95 (C#) Changes in Huo Chess 0.95: 1. Merged the CountScore and CountScore_Human in one function. Also added parameters to the function so as to better control its parameterization. 2. Added functions to find the dangerous squares in the chessboard (see FindAttackers and FindDefenders functions). Added a "danger" check in the CountScore function. This will check whether the piece in a square is threatened by one or more pieces of the opponent (see DangerWeight variable) 3. Removed unused variables to eliminate all warnings. Reach a Maintenance Index of 19 (from 16 in the previous versions). 4. Fixed the program to take into account Danger_penalty (and removed the similar danger_penalty which was used in some places and caused confusion, actually nulling the effect of Danger_penalty in CountScore) 5. Uncomment the "Find value of piece in Skakiera(y,u)" part in ComputerMove, so as to find the value of Value_of_piece_in_square.+++ Size: 57 KB (without GUI), 93 KB (with GUI) 0.93 Versions (only C#) Implemented the MiniMax algorithm for thinking. Two types of versions are distributed: Huo Chess (Kakos-MiniMax edition): A version where the existing Kakos algorithm for searching in depth is used, with the MiniMax algorithm added for the evaluation of the best move. Huo Chess (Simple-MiniMax): A new simpler version where the searching in depth has been written from scratch (in an attempt to "clean" the code). MiniMax algorithm is again used for the evaluation of the best move. In this simpler method, the following changes have taken place: Deleted the CheckMove , CountScore_Human , HumanMove , CheckHumanMove HumanMove_template functions. Reorganized and simply Increased thinking depths from 8 to 20. Reduced the size by using a template ComputerMove function for all thinking depths (instead of ComputerMove2 , ComputerMove4 , ComputerMove6 , etc.). The C++ edition is still in 0.81 version (separate ComputerMove functions still exist) and XNA / VB versions are still in 0.82 versions and are still distributed for educational purposes. Size of C# edition: 52.5 KB. Version 0.82 Changed the ComputerMove , HumanMove , CountScore , ElegxosOrthotitas functions. Increased thinking depths from 8 to 20. Reduced the size by using a template ComputerMove function for all thinking depths (instead of ComputerMove2 , ComputerMove4 , ComputerMove6 etc). The C++ edition is still in 0.81 version (separate ComputerMove functions still exist), for educational purposes. Size of C# edition: 52.5 KB. Version 0.722 XNA Huo Chess (C# edition) with Graphical User Interface based on XNA. Version 0.722 cs Huo Chess port in C# programming language. Version 0.722 Fixed some issues with the HumanMove function. Version 0.721 More thinking depth analysis capability has been added to the program. Huo Chess uses the ComputerMove2 , ComputerMove4 , ComputerMove6 and ComputerMove8 functions to think at a depth of 8 moves (4 half-moves for the white and 4 half-moves for the black pieces). Size: 56.5 KB Version 0.6 (Micro edition) It was based on version 0.6. Reduced all string/text (i.e. "White Rook" => "WR"). Reduced the length of variable names (in every variable in the program, specific strings were replaced with smaller ones). Icon was replaced with smaller one. Removed all unnecessary files (resources, assembly.cpp, stdafx, etc.). Size: 47.5 KB Version 0.6 Removed the empty try...catch statement in line 3959 at HumanMove , which caused the bug at ElegxosNomimotitas not to show. Optimized the CountScore function. Fixed the bug at ElegxosNomimotitas in the part that checks moves of Rook-Queen-Bishop at lines 3143-32173. Added penalty in case the computer moves its pieces next to an opponent' pawn. Made the computer eat the opponent's queen when there is a chance. Lowered the value of the opponent's king, so as to avoid the computer continuously going after him and sacrifice pieces for that. Optimized the CountScore_Human function. Size: 53.5 KB Version 0.5 Fixed HumanMove function. Optimized CountScore and ComputerMove functions. Size: 51.5 KB Version 0.4 Added random playing capability. Optimized CheckForWhiteCheck and CheckForBlackCheck functions. Size: 51.0 KB Version 0.3 Stronger playing capabilities. Added opening book capability. Optimized ElegxosNomimotitas and ElegxosOrthotitas functions. Size: 91.5 KB Version 0.2 Fixed some bugs due to which computer played illegal moves. Thanks to everyone who gave me feedback! Size: 99.5 KB Version 0.1 Initial version. Known problems: the program plays some illegal moves sometimes. Not too strong at all. 49.0 KB

++Keep coding!