📜 ⬆️ ⬇️

Anatomy of Melancholia

Knowledge is power.

Francis Bacon.

... in much wisdom much sorrow;
and who multiplies knowledge, multiplies sorrow.
')
Book of Ecclesiastes.

Games live their lives. They arise from nowhere, develop, generate new games, are forgotten by everyone and, sometimes, come back from oblivion. There are many examples in the history of games that were defeated in this process of natural selection. These are the various variants of Shogi , which have survived to this day only thanks to the reverent attitude of the Japanese people to their cultural heritage. A party to a game similar to Taikyoku shogi could drag on for months (if not for years). But these chess dinosaurs of the Heian epoch are not the brightest representatives of the fossil world of board games.

I want to talk about a truly amazing game. The threats in it are not obvious, and the goals are not trivial. You can win in many ways, but playing is not easy at all. It can not be attributed either to the Checkers family or to the chess family. Its origin is foggy. For the average man in the street, this game is about as exciting as it is drawing up magic squares . It fully justifies one of its names - only philosophers can play it.

For the first time, the Rithmomachia , also known as the “Battle of Numbers” or “The Game of the Philosophers”, was described, approximately, in 1030 by a monk named Asilo. Authorship, apparently baseless, is attributed to Plato , and the rules of the game are based on Boethius' arithmetic theory. Subsequently, the rules of the game were marginally changed by another monk, named Hermannus Contractus , who added notes on the theory of music. For a long time, Ritmomachia was used as a teaching tool in teaching students to mathematics. Intellectuals of that time played Ritmomachia for pleasure (at one time it was even more popular than Chess), Robert Burton mentioned it in Anatomy of Melancholy , and Thomas More considered this game a good leisure for the inhabitants of his Utopia . Then, suddenly, it was all over. The interests of mathematics and Ritmomachia diverged and the game was forgotten. Of course, this does not mean that we can not remember it.

Rules of the game


Two players play Ritmomachia on a rectangular 8x16 square board. Turns are performed alternately. For each move, one piece moves. As a result of a move, one or more opponent pieces may be taken (removed from the board). The following types of shapes are defined:




Ignoring the numerical marks on the figures (I’ll talk about them below), you can see that two types of moves are allowed. In the first case, the figure moves in a straight line and can be stopped by any other figure (one’s own or an opponent) on the way. Thus the Circle (one cell diagonally), as well as the Triangle (strictly two cells orthogonal) and Square (strictly three cells orthogonal) are moved. Another possibility is to “jump” the pieces to the target cell, similar to the knight's move in chess. So can walk the Triangle or Square. Any move should be performed only on an empty square.

A unique figure is the Pyramid. Actually it is not a figure, but a set of figures. Each piece in the set (Circle, Triangle or Square) allows the Pyramid to perform the appropriate type of turn. The figure above shows the possible moves of the Pyramid in the "top of the range". As it is easy to guess, by the end of the game, the “completeness” can be greatly disturbed (by the way, the Pythmid from Ritmomachia is the only figure I know that can be “killed in parts”).

I will dwell on the methods of "murder". There are four of them in Ritmomachia:




The most radical is the capture of the figure "under siege." The figure will be removed if after the next turn it turns out to be blocked in all diagonal or orthogonal directions. If the figure is located at the edge of the board or in the corner - you will have to block fewer directions. This is the simplest (but not the only) way to take the Pyramid all at once - entirely. Taking “under siege” is the only way that does not use the numerical values ​​of the figures.

Another way is to take a figure with a matching numeric value. If, after completing the move, on the field, where the next move could move the figure (if this field was empty, of course) is the figure of the enemy, with the same numerical value, it will be taken. It should be noted that this method of combat may be asymmetric. So (in the picture above), after White’s move, the Circle, with a numerical value of 16, can take a black Triangle with the same value, but the Triangle, on its own turn, cannot do this, since it goes to other cells. At the same time, white and black triangles with numerical values ​​of 25 beat each other symmetrically (which figure will be taken is determined by the order of the turn).

An improved version of the previous one is the following method. This time, “from ambush”, two figures are attacking. If the sum, difference, product or quotient of their numerical values ​​coincide with the numerical value on the opponent's figure - the figure is removed. This is the most frequently used possibility of fighting figures in the game, but it may be far from easy to see all such threats in a less complex position.

The latter method allows you to beat the pieces in the far distance. If the result of the product or the particular numerical value of the figure and the distance to the opponent’s figure (orthogonal) coincides with the numerical value of the opponent’s figure - the figure is removed. Other figures located in the direction of the battle, do not prevent the threat. When calculating the distance, the starting and ending positions are taken into account. This is a symmetric method of combat (since it does not depend on the rules for moving figures). The sequence of the turn determines which of the pieces will be shot.

It remains to talk about how these rules apply to the Pyramids. With this, everything is simple - in any calculations for taking figures, the Pyramid can act either as an independent figure (its numerical value coincides with the sum of the numerical values ​​of all its components), or as any of its parts. Similarly, the Pyramid can be taken as a whole (by its total value) or in parts (each of its components is vulnerable separately). Given the rules for moving the Pyramid, which I mentioned above, this makes the Pyramid the strongest (and most vulnerable) figure.



The initial placement of figures is associated with another important feature of the game. Although the set of figures is the same, the numerical values ​​differ. Also, the “equipment” of the pyramids is different (shown on the side of the board, for each of the players). This makes the game asymmetric. Tactics and strategies, for the black player are not suitable for white (and vice versa). This makes the game more interesting.

Glorious victories


The rules described above are enough to start playing Ritmomachia, but if the goal of the game was to simply capture all the pieces of the opponent, the game would not be so interesting. Yes, you can win in this way, but this is not the only (and not the best) way to win! I really like the game, the purpose of which is not the straightforward "killing" of enemy figures. So in Chess, to win, it is not at all necessary to “eat” all the figures, it’s enough to checkmate the King! In some versions of Hasami Shogi , the goal of the game is even more unexpected (to win, you need to build a line of 5 of your chips in a row). Ritmomachia does not disappoint in this regard. Here is a list of ways to win in this game (in order of increasing their "gloriousness"):


Here you can distinguish between two fundamentally different types of game objectives. Common Victories are associated with the capture of enemy pieces (with the possible limitation of the number of captured pieces, in order to protect against aggressive play). Proper Victories are considered a more worthy conclusion and are associated with the re-creation of a certain “harmony” on the territory of the enemy (which does not exclude the possibility of a battle of enemy figures).



The examples above illustrate that enemy figures can (and should) be involved in “harmony”. In the first case, an arithmetic progression was constructed (16, 36, 56). The second example combines geometric (4, 12, 36) and harmonic (4, 6, 12) progression. In the third case, there are all three types of progression (you can find them yourself). In case there are difficulties with an oral account, tables of all possible solutions in Ritmomachia are built (however, people who have problems with an oral account are unlikely to be interested in Rhythmomachia).

Possible options


In its heyday, ritmomachia has already undergone many changes. Subsequent renovations did not improve the situation. There are many discrepancies in the descriptions of the rules of this game. Some sources give alternative versions of the original arrangement of pieces (I even came across a variant describing the game on a 8 x 14 board):



There are options with simplified rules for moving figures. According to these rules, the figures move in a straight line (orthogonal and diagonal) for a given number of cells. Any figure in the path of movement stops moving. Jumps, like the course of the Horse in Chess, are absent. The pyramid, as usual, combines the moves present in its set of figures.



In addition, there is a variant with the usual rules of the movement of figures, in which a special move of the Pyramid of 3 cells along the diagonal is added. Such a move is possible under the condition that none of the figures originally included in the Pyramid were taken. Another option is associated with the preservation of the initial set of figures of the Pyramid. It is allowed to use the total value of the Pyramid, only under the condition that none of its figures have yet been “eaten”. There are more simple options in which the Pyramid is removed completely if any of its components is attacked.

Some of the options associated with changes in the rules of taking figures. So, in one of the options, to capture a " by Siege " figure (siege), it is necessary to block all directions of its "natural" movement. At the same time, it is not necessary to place your pieces close to the blocked one (the examples below illustrate the concept). This is quite an interesting rule.



There is an option in which the capture of " by Ambush " (ambush) is carried out like games of a "clamping" type (in this case, the rules for moving figures are ignored):



There are a large number of discrepancies in the definitions of " Glorious Victories ". Often, the conditions of such victories include the preliminary destruction of the enemy's Pyramid. Also, there is a condition for completing the game, limited to taking the Pyramid. There are many variations of this game. Acquaintance with the nuances of the rules, before the start of the game, in any case, is not at all superfluous.

Details for the curious


The implementation of the game is naturally divided into several stages. The easiest way was to realize the movement of figures. From this I started. Here is how the squares look like:

Moving Squares
: leap-n ( 'leap-dir 'shift-dir count ? -- ) IF BEGIN OVER EXECUTE IF on-board? IF 1- DUP 0> IF FALSE ELSE 2DROP TRUE TRUE ENDIF ELSE 2DROP FALSE TRUE ENDIF ELSE 2DROP FALSE TRUE ENDIF UNTIL IF EXECUTE IF on-board? empty? AND IF from here DUP last-position ! move capture-all add-move ENDIF ENDIF ELSE DROP ENDIF ELSE 2DROP DROP ENDIF ; : shift-n ( 'shift-dir count ? -- ) IF BEGIN OVER EXECUTE IF on-board? empty? AND IF 1- DUP 0> IF FALSE ELSE 2DROP TRUE TRUE ENDIF ELSE 2DROP FALSE TRUE ENDIF ELSE 2DROP FALSE TRUE ENDIF UNTIL IF from here DUP last-position ! move capture-all add-move ENDIF ELSE 2DROP ENDIF ; : s-move-n ( -- ) ['] North 3 TRUE shift-n ; : s-move-s ( -- ) ['] South 3 TRUE shift-n ; : s-move-w ( -- ) ['] West 3 TRUE shift-n ; : s-move-e ( -- ) ['] East 3 TRUE shift-n ; : s-move-nw ( -- ) ['] Northwest ['] North 2 TRUE leap-n ; : s-move-ne ( -- ) ['] Northeast ['] North 2 TRUE leap-n ; : s-move-sw ( -- ) ['] Southwest ['] South 2 TRUE leap-n ; : s-move-se ( -- ) ['] Southeast ['] South 2 TRUE leap-n ; : s-move-wn ( -- ) ['] Northwest ['] West 2 TRUE leap-n ; : s-move-ws ( -- ) ['] Southwest ['] West 2 TRUE leap-n ; : s-move-en ( -- ) ['] Northeast ['] East 2 TRUE leap-n ; : s-move-es ( -- ) ['] Southeast ['] East 2 TRUE leap-n ; {moves s-moves {move} s-move-n {move} s-move-s {move} s-move-w {move} s-move-e {move} s-move-nw {move} s-move-ne {move} s-move-sw {move} s-move-se {move} s-move-wn {move} s-move-ws {move} s-move-en {move} s-move-es moves} 


Some difficulty has arisen with the Pyramids, since their set of moves is determined by which pieces belong to them at the moment. It was necessary to describe all possible moves for them, and when making a move, check the presence in the set of the corresponding figure:

Moving the Pyramids
 : is-correct-type? ( piece-type -- ? ) not-empty? IF piece-type PYRAMID > IF here SWAP a1 to BEGIN friend-p IF DUP is-piece-type? IF DROP TRUE TRUE ELSE FALSE ENDIF ELSE DROP FALSE TRUE ENDIF UNTIL SWAP to ELSE DROP FALSE ENDIF ELSE DROP FALSE ENDIF ; : pr-move-ne ( -- ) ['] Northeast ROUND is-correct-type? leap-0 ; : pr-move-se ( -- ) ['] Southeast ROUND is-correct-type? leap-0 ; : pr-move-nw ( -- ) ['] Northwest ROUND is-correct-type? leap-0 ; : pr-move-sw ( -- ) ['] Southwest ROUND is-correct-type? leap-0 ; {moves p-moves {move} pr-move-ne {move} pr-move-se {move} pr-move-nw {move} pr-move-sw ... moves} 


The implementation of the battle of figures turned into a real "race for survival." It was necessary to implement four completely different (and very nontrivial) ways of taking. At the same time, I wanted to limit the number of field views in order to minimize the overhead of calculations (the course is already calculated ugly for a long time). As a result, I settled on the idea of ​​pre-filling several arrays, with the implementation of subsequent checks on the conditions of the battle of figures:

Ways to fight
 MAXV [] attacking-values[] MAXS [] current-positions[] MAXS [] current-values[] MAXS [] eruption-values[] : fill-current ( pos -- ) 1 current-count ! DUP 0 current-positions[] ! DUP piece-type-at PYRAMID > IF a1 to 0 BEGIN enemy-p IF not-empty? IF piece piece-value current-count @ MAXS < IF here current-count @ current-positions[] ! DUP current-count @ current-values[] ! current-count ++ ENDIF + ENDIF FALSE ELSE TRUE ENDIF UNTIL ELSE DUP piece-at piece-value ENDIF 0 current-values[] ! to ; : get-eruption-values ( n -- ) 0 sum-value ! PYRAMID is-piece-type? IF here a1 to BEGIN friend-p IF not-empty? eruption-count @ MAXE < AND IF OVER piece piece-value DUP sum-value @ + sum-value ! * eruption-count @ eruption-values[] ! eruption-count ++ ENDIF FALSE ELSE TRUE ENDIF UNTIL to sum-value @ ELSE piece piece-value ENDIF eruption-count @ MAXE < IF * eruption-count @ eruption-values[] ! eruption-count ++ ELSE 2DROP ENDIF ; : get-attacking-values ( piece-type -- ) 0 sum-value ! FALSE sum-flag ! PYRAMID is-piece-type? IF here a1 to BEGIN friend-p IF not-empty? attacking-count @ MAXV < AND IF piece piece-value sum-value @ + sum-value ! OVER is-piece-type? IF TRUE sum-flag ! piece piece-value attacking-count @ attacking-values[] ! attacking-count ++ ELSE ENDIF ENDIF FALSE ELSE TRUE ENDIF UNTIL to DROP sum-flag @ attacking-count @ MAXV < AND IF sum-value @ attacking-count @ attacking-values[] ! attacking-count ++ ENDIF ELSE is-piece-type? attacking-count @ MAXV < AND IF piece piece-value attacking-count @ attacking-values[] ! attacking-count ++ ENDIF ENDIF ; : check-siege-od ( 'dir -- ) EXECUTE IF predict-move on-board? NOT friend? OR IF siege-counter -- ENDIF on-board? friend? AND IF 2 get-eruption-values ENDIF to ELSE siege-counter -- ENDIF ; : check-siege-dd ( 'dir -- ) EXECUTE IF predict-move on-board? NOT friend? OR IF siege-counter -- ENDIF on-board? friend? AND IF ROUND get-attacking-values ROUND check-equality-piece ENDIF to ELSE siege-counter -- ENDIF ; : check-siege ( pos -- ) 4 siege-counter ! DUP to ['] North check-siege-od DUP to ['] South check-siege-od DUP to ['] West check-siege-od DUP to ['] East check-siege-od siege-counter @ 0= IF TRUE is-captured? ! ENDIF 4 siege-counter ! DUP to ['] Northeast check-siege-dd DUP to ['] Southeast check-siege-dd DUP to ['] Northwest check-siege-dd DUP to ['] Southwest check-siege-dd siege-counter @ 0= IF TRUE is-captured? ! ENDIF to ; : check-equality-dd ( 'second-dir count 'first-dir -- ) EXECUTE on-board? AND IF BEGIN 1- DUP 0< IF TRUE ELSE OVER EXECUTE on-board? AND IF predict-move friend? IF OVER count-to-piece-type DUP get-attacking-values check-equality-piece ENDIF to FALSE ELSE TRUE ENDIF ENDIF UNTIL 2DROP ELSE 2DROP ENDIF ; : check-equality-od ( 'second-dir count 'first-dir -- ) EXECUTE on-board? AND empty? AND IF BEGIN 1- DUP 0< IF TRUE ELSE OVER EXECUTE on-board? AND IF predict-move friend? IF OVER count-to-factor get-eruption-values OVER count-to-piece-type DUP get-attacking-values check-equality-piece TRUE ELSE not-empty? ENDIF SWAP to ELSE TRUE ENDIF ENDIF UNTIL 2DROP ELSE 2DROP ENDIF ; : check-equality ( pos -- ) DUP to ['] North 2 ['] North check-equality-od DUP to ['] North 2 ['] Northwest check-equality-dd DUP to ['] North 2 ['] Northeast check-equality-dd DUP to ['] South 2 ['] South check-equality-od DUP to ['] South 2 ['] Southwest check-equality-dd DUP to ['] South 2 ['] Southeast check-equality-dd DUP to ['] West 2 ['] West check-equality-od DUP to ['] West 2 ['] Northwest check-equality-dd DUP to ['] West 2 ['] Southwest check-equality-dd DUP to ['] East 2 ['] East check-equality-od DUP to ['] East 2 ['] Northeast check-equality-dd DUP to ['] East 2 ['] Southeast check-equality-dd to ; : check-ambush-prod ( value -- ? ) value-1 @ value-2 @ * OVER = IF DROP TRUE ELSE DUP value-1 @ * value-2 @ = IF DROP TRUE ELSE value-2 @ * value-1 @ = IF TRUE ELSE FALSE ENDIF ENDIF ENDIF ; : check-ambush-cond ( value -- ? ) value-1 @ value-2 @ + OVER = IF DROP TRUE ELSE DUP value-1 @ + value-2 @ = IF DROP TRUE ELSE DUP value-2 @ + value-1 @ = IF DROP TRUE ELSE check-ambush-prod ENDIF ENDIF ENDIF ; : check-ambush-pair ( -- ) current-count @ BEGIN 1- DUP current-positions[] @ 0< NOT IF DUP current-values[] @ check-ambush-cond IF DUP 0> IF DUP current-positions[] @ DUP enemy-at? IF DUP ChangePieces capture-at ELSE DROP ENDIF -1 OVER current-positions[] ! ELSE TRUE is-captured? ! ENDIF ENDIF ENDIF DUP 0> NOT UNTIL DROP ; : check-ambush ( -- ) attacking-count @ BEGIN 1- attacking-count @ BEGIN 1- 2DUP < IF 2DUP attacking-values[] @ value-1 ! attacking-values[] @ value-2 ! check-ambush-pair ENDIF DUP 0> NOT UNTIL DROP DUP 0> NOT UNTIL DROP ; : fill-eruption-values ( 'dir pos n -- ) value-1 ! to 1 BEGIN 1+ OVER EXECUTE IF predict-move OVER value-1 @ > on-board? friend? AND AND IF OVER get-eruption-values ENDIF to FALSE ELSE TRUE ENDIF UNTIL 2DROP ; : check-eruption-pair ( -- ) current-count @ BEGIN 1- DUP current-positions[] @ 0< NOT IF DUP current-values[] @ value-1 @ = IF DUP 0> IF DUP current-positions[] @ DUP enemy-at? IF DUP ChangePieces capture-at ELSE DROP ENDIF -1 OVER current-positions[] ! ELSE TRUE is-captured? ! ENDIF ENDIF ENDIF DUP 0> NOT UNTIL DROP ; : check-eruption-values ( -- ) eruption-count @ BEGIN 1- DUP eruption-values[] @ value-1 ! check-eruption-pair DUP 0> NOT UNTIL DROP ; : check-eruption ( pos -- ) ['] North OVER 4 fill-eruption-values ['] South OVER 4 fill-eruption-values ['] West OVER 4 fill-eruption-values ['] East OVER 4 fill-eruption-values to check-eruption-values ; : capture-all ( -- ) here ROWS COLS * BEGIN 1- DUP on-board-at? OVER enemy-at? AND IF 0 attacking-count ! 0 eruption-count ! FALSE is-captured? ! DUP fill-current DUP check-siege is-captured? @ NOT IF DUP check-equality ENDIF is-captured? @ NOT IF check-ambush ENDIF is-captured? @ NOT IF DUP check-eruption ENDIF is-captured? @ IF capture-piece ENDIF ENDIF DUP 0> NOT UNTIL DROP to ; 


The capture of figures in the Pyramid, as well as the displacement, had to be processed in a special way:

Taking figures
 : capture-piece ( -- ) current-count @ BEGIN 1- DUP 0> IF DUP current-positions[] @ DUP 0< NOT IF DUP enemy-at? IF DUP ChangePieces capture-at ELSE DROP ENDIF ELSE DROP ENDIF FALSE ELSE DUP current-positions[] @ DUP enemy-at? IF DUP ChangePieces capture-at ELSE DROP ENDIF TRUE ENDIF UNTIL DROP ; 


Already in the process of debugging, I realized that all the checks are performed at the time before the progress. I had to write a small function imitating the movement of the figure (this is not a complete recalculation of the position on the board, but I managed to get by with a little blood):

Position change
 : predict-move ( -- pos ) here DUP from = IF last-position @ to ELSE DUP last-position @ = IF from to ENDIF ENDIF ; 


Against the background of all these horrors, checking the conditions for completing the game seemed like a trivial task. There was a small amount of Axiom magic, but there is nothing here that I would be afraid to show my parents:

Completion check
 15 CONSTANT WINC 1315 CONSTANT WINW 984 CONSTANT WINB : WhitePieces++ ( -- ) WhitePieces ++ ; : BlackPieces++ ( -- ) BlackPieces ++ ; : WhiteValues++ ( -- ) WhiteValues ++ ; : BlackValues++ ( -- ) BlackValues ++ ; : ChangePieces ( pos -- ) DUP piece-at piece-value SWAP player-at White = IF COMPILE WhitePieces++ BEGIN 1- COMPILE WhiteValues++ DUP 0> NOT UNTIL DROP ELSE COMPILE BlackPieces++ BEGIN 1- COMPILE BlackValues++ DUP 0> NOT UNTIL DROP ENDIF ; : OnIsGameOver ( -- gameResult ) #UnknownScore current-player White = IF WhitePieces @ WINC >= IF DROP #LossScore ENDIF WhiteValues @ WINW >= IF DROP #LossScore ENDIF ENDIF current-player Black = IF BlackPieces @ WINC >= IF DROP #LossScore ENDIF BlackValues @ WINB >= IF DROP #LossScore ENDIF ENDIF ; 


From this point on, the program could already play (although it did it rather passively). The fact is that, in the absence of an evaluation function (and the custom implementation of AI), Axiom tries to perform a complete search, to a terminal position. It is clear that the completion of the game is far beyond the horizon of the possible depth of enumeration, as a result of which, the found moves are not particularly meaningful. In general, it remains to add AI teeth:

Evaluation function
 : OnEvaluate ( -- score ) current-player material-balance ; 


Here, a very convenient material-balance function (provided by Axiom) is used, which uses the weight values ​​specified for the figures (the same values ​​are used for the implementation of the Rhythmochial rules):

Description of figures
 {pieces {piece} R0 {moves} r-moves 0 {value} {piece} R1 {moves} r-moves 1 {value} {piece} R2 {moves} r-moves 2 {value} {piece} R3 {moves} r-moves 3 {value} {piece} R4 {moves} r-moves 4 {value} {piece} R5 {moves} r-moves 5 {value} {piece} R6 {moves} r-moves 6 {value} {piece} R7 {moves} r-moves 7 {value} {piece} R8 {moves} r-moves 8 {value} {piece} R9 {moves} r-moves 9 {value} {piece} R16 {moves} r-moves 16 {value} {piece} R25 {moves} r-moves 25 {value} {piece} R36 {moves} r-moves 36 {value} {piece} R49 {moves} r-moves 49 {value} {piece} R64 {moves} r-moves 64 {value} {piece} R81 {moves} r-moves 81 {value} {piece} T0 {moves} t-moves 0 {value} {piece} T6 {moves} t-moves 6 {value} {piece} T9 {moves} t-moves 9 {value} {piece} T12 {moves} t-moves 12 {value} {piece} T16 {moves} t-moves 16 {value} {piece} T20 {moves} t-moves 20 {value} {piece} T25 {moves} t-moves 25 {value} {piece} T30 {moves} t-moves 30 {value} {piece} T36 {moves} t-moves 36 {value} {piece} T42 {moves} t-moves 42 {value} {piece} T49 {moves} t-moves 49 {value} {piece} T56 {moves} t-moves 56 {value} {piece} T64 {moves} t-moves 64 {value} {piece} T72 {moves} t-moves 72 {value} {piece} T81 {moves} t-moves 81 {value} {piece} T90 {moves} t-moves 90 {value} {piece} T100 {moves} t-moves 100 {value} {piece} S0 {moves} s-moves 0 {value} {piece} S15 {moves} s-moves 15 {value} {piece} S25 {moves} s-moves 25 {value} {piece} S28 {moves} s-moves 28 {value} {piece} S36 {moves} s-moves 36 {value} {piece} S45 {moves} s-moves 45 {value} {piece} S49 {moves} s-moves 49 {value} {piece} S64 {moves} s-moves 64 {value} {piece} S66 {moves} s-moves 66 {value} {piece} S81 {moves} s-moves 81 {value} {piece} S120 {moves} s-moves 120 {value} {piece} S121 {moves} s-moves 121 {value} {piece} S153 {moves} s-moves 153 {value} {piece} S169 {moves} s-moves 169 {value} {piece} S225 {moves} s-moves 225 {value} {piece} S289 {moves} s-moves 289 {value} {piece} S361 {moves} s-moves 361 {value} {piece} P0 {moves} p-moves 0 {value} {piece} P91 {moves} p-moves 91 {value} {piece} P190 {moves} p-moves 190 {value} pieces} 


This implementation lacks a lot (for example, checking the end of the game for Glorious Victories ). I will try to add the missing functionality in the future. The current version of the source can always be found here .

What is the result?


Ritmomachia interested me, first of all, in its complexity. Of course, there were no thoughts to implement it on ZRF . I had to master Axiom , for this! Currently, there is a simplified implementation that does not support Glorious Victories . Also, there is no firm certainty that I found all the errors in the code (1000 lines in ForthScript is serious). This is a beta version, but in general, it works:



You may notice that the game ends very quickly. It really is. In case both players play aggressively, under the conditions of Common Victories (and without limiting the number of pieces taken), the average duration of the game is ~ 10 moves. In this case, the first player has a significant advantage:

 Cumulative results following game 13 of 100: Player 1 "Eval", wins = 13. Player 2 "Eval", wins = 0. Draws = 0 

It's funny that if only one of the players is aggressive, the game is delayed to more than 300 moves (the aggressive player almost always wins).

 Cumulative results following game 22 of 100: Player 1 "Rithmomachy", wins = 1. Player 2 "Eval", wins = 21. Draws = 0 

It's hard to play with a computer. Even with the illumination of the figures that are under combat, it is not easy to figure out which particular figure should make a move in order to accomplish the capture (at the same time, it is desirable not to substitute your own figures). In our time, this game is unlikely to be popular, but one does not take away from her. She is great at developing oral skill.

Source: https://habr.com/ru/post/234587/


All Articles