Log In  

Cart #sweezechess-7 | 2020-07-17 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
8

ive made a surprisingly functional chess port over the course of the last 2 days
i tried doing this in turing like half a year ago but couldn't get check detection down
this time it actually worked

it comes with 4 modes of difficulty:
0 - random
1 - captures when possible
2 - makes trades
3 - avoids checkmate

according to an elo tester, level 3 is rated around 1530
i don't trust this number, the test comprised of 10 questions and i ranked 1615 despite having a chess.com elo of 612 (:

the difficulty, plus "many" more things, can be changed in the pause menu

update 1.1:

singular bug fix, some quality of life improvements, as well as an ai with 3 varying levels of "difficulty"

level 0: entirely random moves
level 1: captures if it can
level 2: will try avoid unfavourable captures

there are also a bunch of pause menu options to disable or adjust the ai, as well as change which colour you play as

i also added in a preventative measure to stop you from castling out of, into, or through check


update 1.2:

it's been, what, 2 hours? well, i've added a bunch of stuff

all quality of life stuff, plus fixing start colour selection

this includes:
-an indicator showing where the opponent last moved
-overhauled sound effects + a bunch of new ones
-little jingle at the start
-wave sounds for background noise because composing is hard


update 1.3:

there was no 1.3 i just wanted the decimal to match the cart version

update 1.4:

i've added a bunch of little fixes, plus a third ai level
this one takes roughly 20 seconds to think, but it is significantly harder to kill i hope
there's a little loading screen so you can see its progress while you wait, at least
gonna be honest, this was meant to be an add-on to ai 2 but i feel like the tradeoff between 20 seconds of wait time and not being able to scholar's mate this thing should be an optional thing

update 1.5:

-fixed a bug preventing pawn promotion
-significantly slashed the wait time for difficulty 3
-added palette swapping
-removed herobrine

update 1.6:

this one's a bug fix update, primarily
thank you to remcode for giving me the detailed descriptions of the crashes, other people i've had playtest encountered them before but i didn't get the boards from either.

the bug in v1.4 was an issue with how it evaluated the board. its position was so abysmal that it went below the minimum value i used to find any move, and so it resulted in having no moves to choose from

the bug in v1.5 was actually due to pretty much the opposite thing! i had actually found that bug in v1.4 before i published v1.5, and to combat it, i changed the minimum value from -9999 to -999999. little did i know, pico8 underflows at that point and it turned into a positive number, rendering it useless
i have 0 clue why i didn't see that earlier
anyways, now it looks for -22222/22222 as its max so there isn't any more over/underflow

i also made it like to play 1.e5 e4 if it can


update 1.7:

this one fixes no bugs! it just makes things a little better
-ending a game no longer actually resets the cart, keeping your settings
-the palette swap options have been revamped into a menu
-resignation is now available on the pause menu, to end the game early without rebooting the cart

P#79289 2020-07-14 01:39 ( Edited 2020-07-17 20:18)

1

Very well done, an impressive feat of programming capability!

P#79298 2020-07-14 08:17
1

August 2022

To anyone looking to play this, please see lower down this thread where I have uploaded a version with a number of bugfixes.

The original (above) works, but is unintentionally a little random in some of its moves.


July 2020

I played a game and am really quite impressed. On the occasions I have begun to program it, I normally get distracted by other projects before I implement any AI for chess (with, at time of original post, one exception which had AI but not a strong game).

Unfortunately, I had a runtime error occur after the computer player (v1.4) got in a tight spot.

Click show for error details.

Hopefully I have typed this all in correctly:

Runtime error line 144 tab 6
add(cut,tonum(sub(splice,i,i)))
in split line 144 (tab 6)
in ai_two line 115 (tab 6)
in deeper line 133 (tab 6)
in ai_set line 19 (tab 6)
in _update line 104 (tab 0)
at line 0 (tab 0)

If it will help any in tracking it down:

Final position in Forsyth-Edwards Notation:

2b1qb2/rpppkp2/8/p3P1Q1/2B5/2P5/PP3PPP/RN3RK1 b - - 0 14

https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation

From memory, in Portable Game Notation:

[Date "2020.07.17"]
[White "Remcode"]
[Black "SWEEZEchess 1.4"]
[Result "1-0"]

  1. e4 e5 2. Nf3 Nc6 3. Bc4 a5 4. O-O Ra7 5. c3 h6 6. d4 exd4 7. Nxd4 Nxd4 8. Qxd4 Ra8 9. e5 Ke7 10.Qh4+ g5 11. Bxg5+ hxg5 12. Qxh8 Ra7 13. Qxg8 Qe8 14. Qxg5+ 1-0

https://en.wikipedia.org/wiki/Portable_Game_Notation

Similarly (v1.5), in the hope that posting these helps you to track them down:

(Game unretrievable.) Computer was forced into a situation where to avoid check from a rook on a8 it would have had to move a bishop from d7 to c8.

Runtime error line 186 tab 6
board[cut[4]][cut[3]]=board[cut[2]][cut[1]]
Attempt to index field '?' (a nil value)
in modifgister line 186 (tab 6)
in ai_two line 99 (tab 6)
in deeper line 146 (tab 6)
in ai_set line 19 (tab 6)
in _update line 122 (tab 0)
at line 0 (tab 0)

Update:

20/7/2020 Everything seems fixed by version 1.7

I have played a few more games without any crashes. The computer gives a varied game, and is quite pleasant to play against (which is to say that unlike competition level chess computers it doesn't crush me).

Minor tactical spoilers:

It has weaknesses; in particular in the opening it moves its edge pawns and rooks and king more than it should, which means a strong central development and attacks on points near the king tend to lead to its defeat quite quickly.

P#79485 2020-07-17 07:49 ( Edited 2022-08-31 14:49)

Nice this is really underated

P#79696 2020-07-21 00:55

@MEINsweeze

I think it's the first time I've had an en passant situation against SWEEZEchess.

There is a minor bug in the rules logic; it won't occur often, but the logic should allow a player to capture en passant to get out of check.

The following position is white to move after black plays pawn d7-d5+. White should be able to play cxd6 en passant (if white wanted to), but the computer does not recognise it.

White: remcode
Black: SWEEZEChess 1.7

(The above position was after a deliberately unusual opening for variety, with 1. b4 Nf6 2. c4 allowing some interesting play later after ... a5; Qa4 pinning the black a-pawn to the rook and the black d-pawn to the king, and e3 to allow both bishops to move to the queenside to target the black kingside.)


Non-authoritative reference:https://en.wikipedia.org/wiki/En_passant#Unusual_examples

"In the diagram, if Black plays 1...g5+, ... Black overlooks that White can counter this check with the en passant capture 2. fxg6e.p.#"

P#79710 2020-07-21 11:05
4

Cart #sweezechess_1_7_mod1-6 | 2022-08-31 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
4

SWEEZEchess 1.7/4

SWEEZEchess 1.7/4 is primarily a bugfix (by remcode) for MEINsweeze's SWEEZEchess 1.7. In 2020 an additional AI was added providing two new levels. In 2022 this has now been updated to be faster, and so the number of levels has been increased again.

Change to levels (and other settings) is accessed through the pause menu.

The AI have been added in such a way that they can all be easily backed out should MEINsweeze want the bugfixes but not the additional AI.

AI 4 through 8

General overview of AI 4 through 8

These AI opponents were written to play with a level somewhere between SWEEZEchess's existing AI and Krystman's CheckMate AI. There was neither an intention to provide an AI comparable to Krystman's, nor (when originally written) to provide an AI much stronger than SWEEZEchess level 3.

At level 4 the AI should provide a game of about the same level as MEINsweeze's AI 3, but might be a little less aggressive. Levels 5 and 6 should should hopefully still be suitable in speed and strength for a fairly casual game. All the AI from level 2 through to level 8 use the same worth tables so should have some similarity to their gameplay.

An estimate of thinking time per move at different levels is:

  • level 4: 40 seconds
  • level 5: 1 minute 30 seconds
  • level 6: 3 minutes
  • level 7: 6 minutes
  • level 8: 20 minutes

As positions become more complex or harder to judge, this will of course increase. Some rudimentary feedback is given on screen about their thinking process.

AI 3 will play faster game than the other AI, so if you're looking for a quick casual game, MEINsweeze's AI 3 is still likely the more suitable choice. AIs 4 through 8 will play slightly better in some given situations, but are ultimately limited by the shallow depth of their search and thus by not following exchanges through to their conclusion.

Technical overview of AIs 4 through 8

The extra AI opponents share a minimax algorithm which uses alpha-beta pruning and movelist optimisation based on good moves already found at the same depth of search.

They use this algorithm with different settings for standard depth of search, extended depth of search in lines with captures and/or checks, randomness, and amount they pay attention to activity in the scoring.

More on those settings - the standard depth of search is either two or three half moves. The extended depth of search gives a maximum depth from three to six half moves. The randomness is a random value from zero upto (depending on the settings) either about a third the worth of a pawn or the worth of a pawn, and this is added to the rating of a position so that near-best lines may get chosen instead of those the AI would have rated best without the randomness. The amount the AI pay attention to activity in the scoring is minimal in all cases.

For anyone thinking of programming a chess AI, hopefully ai_four (which is what all the AI from level 4 through level 8 are) can give you an idea of possible program structure (although it is by no means definitive). Minimax and negamax and alpha-beta pruning are well worth researching for a chess AI.

The 2022 update to ai_four is intended to make it faster, primarily through movelist optimisation and also by changing how not quite best moves can be selected as best. With the extra speed comes extra depth, and with the extra depth of search comes extra strength.

ai_four was written with clarity in mind - so as to provide anyone looking at the code a greater chance of understanding how it arrives at its decision. This is mainly to align with pico-8 encouraging new coders to look at code and learn from it. I hope the 2022 update to the 2020 release of ai_four is marginally clearer than it was.

Removing the additional AI

@MEINsweeze ai_four and upwards can be easily removed from SWEEZEchess - simply remove the last two tabs (7 and 8), change the ai_level in _init, and make the appropriate adjustments to your own functions deeper (tab 6) and cycle_level (tab 0). The added minimax AI was carefully added in such a way that if you should want to remove it, it would be easily possible to do so; this will allow you to keep the bugfixes without having someone else's AI in your program.

Notes

In reverse chronological order:

2022

31st August: ai_four faster, and with more preset levels

Additionally, made the feedback to the human player nicer. This is present in levels 4 through 8.

The board now rotates as appropriate.

I have still opted not to make significant changes to the look and feel and flow of SWEEZEchess. The additional AI were only put in while I was exploring how the original AI functioned so as to track down a couple of minor bugs (long since fixed).

2020

16th March: Minor AI4/5 tweaks
This is version 1.7/3b, because I'm not yet ready to move to version 1.7/4, for which I've been thinking about giving a better flow to the structure in tab 0; this would allow easier adding in of things such as an AI options page. I am considering that when a user goes from the title screen to the game, there should be an intermediary screen - an AI options page. On the AI options page you should be able to press the button again to start the game with the settings as they are, or alternatively set either side to a human player or any of the AIs, then go into the game. This would be quite a large rewrite in comparison to what I have done so far (excepting AI4/5), even larger if I can get the board to flip for whichever human player is playing.


In the meantime, some small tweaks to AI4/5.

A small improvement to value calculation in tab 8, functions ai_four_aimove and ai_four_playermove (as well as an initialisation in ai_four_entry in tab7); this, I suspect, at the expense of some clarity.

 local mlvalue=0
 ai_four_movelistvalue[1]=#movelist/50
 for k,v in pairs(ai_four_movelistvalue) do
  mlvalue+=k*v
 end
 value+=mlvalue

50 is a guessed at value (after trying 5, 10, 20). It means that when a queen moves off the back rank she might add about .28 to her value (about 14 more moves than when sitting on her start square). The risk with this is that because the queen gains the most value by moving from her starting square, the AI player might move her out too early. So a high divisor needs to be used, which will cause this tweak to complement the values in the worth tables but not to overrule them. (20 wasn't all that bad a value, but the queen still came out early.)

There is a compromise here - the code approaches zero-sum without needing to rewrite/duplicate other parts of SWEEZEchess (one side's movelist value will be half a turn behind the other, which isn't perfect, but will suffice for now). To keep it within a half-turn, at any depth in the two functions (ai_four_aimove, ai_four_playermove) within the iteration of the movelist, the value for one side or another is reset to what it should be at that depth whenever the algorithm returns to that point, so next time the algorithm examines a move it has the most recent value available for the opposing side.

To make this marginally more precise would mean generating a 'movelist' for the non-moving side at the same time as generating the movelist for whichever side is moving. It is my opinion this is too large a rewrite to be in scope for me. As such I am going to leave this small imprecision in AI4/5. Also, the small addition to move value calculation as above has complicated the code for ai_four beyond what I intended, which may make what some of the code does less clear. I consider this loss of clarity unfortunate (as stated above, clarity 'mainly to align with pico-8 encouraging new coders to look at code and learn from it') and am likely to stop tweaking it now.

12th March: Minimax AI & Bugfixes

Have added in ai_four and ai_five. While developing them, I captured an AI rook with my bishop; the AI thought about its response, then moved a pawn forwards - simultaneous to which my bishop transformed into an AI rook. During its lookahead, the resurrect a rook via castling with an enemy piece bug had occurred, and after taking back the move (as part of working through its lookahead) the rook remained. I have therefore provided a provisional (hopeful) fix for this castling bug - I hope I have understood the original code correctly and written a correct fix.

As well as this, I have added in code that hopefully (again, so long as I have understood the original code correctly and thus written a correct fix) stops early random pawn moves leading to the loss of a piece (such as a knight or queen).

4th March: Work-in-Progress - Minimax AI

I have programmed a minimax AI for this (AI 4) but am not posting it yet as I intend to put in a few refinements. It's stronger than AI 3, but - deliberately - not ever so much stronger (for example, it's still much weaker than Krystman's CheckMate).

20th February: Improvements

Two simple speed improvements.

6th February: Bugfixes

@MEINsweeze

There were a few minor bugs with version 1.7 that I have (hopefully) fixed.

(I know it has been 9 months or so since you posted your chess program so this post is not particularly timely - I went 6 or so months without using pico-8 or its forums otherwise I might have looked at these bugs sooner.) Hopefully these fixes all work out as I have intended and do not have any negative repercussions for the gameplay etc.

Fix summary

The following is a short summary of the fixes (in approximate reverse chronological order) - more extensive notes are lower down.

Known unfixed issues

  • A pawn should be able to capture enpassant when the king is in check or checkmate, to take a pawn giving check that has just moved two squares forwards, so as to remove the check(mate). At the moment, SWEEZEchess does not implement the removal of check(mate) by capture enpassant.
  • Not fixable, but worth noting: Computer player (AI 3 [and now AI 4 and 5]) responds to pins (such as bishop pinning a knight to a king) and attacked trapped pieces (such as a bishop on c8 with pawns on b7 and d7 and an enemy knight on b6) by needlessly "sacrificing" other (minor) pieces. [Edit (4th March): I suspect this is because the lookahead is limited, so the computer concludes that whatever happens it is going to lose that much value from the board, so any move that loses that much value then becomes acceptable; the mitigation is to increase the lookahead - which might be difficult with the way it is currently programmed. Mitigation, not solution - see Horizon Effect.]

  • Rare bug seen (March 13th) but I suspect this was a temporary Pico-8 glitch rather than a SWEEZEchess problem:
 RUNTIME ERROR LINE 106 TAB 6  
    IF QLOG[J] < HIGH THEN  
 ATTEMPT TO COMPARE NIL WITH NUMBER  
 IN AI_TWO LINE 106 (TAB 6)  
 IN DEEPER LINE 220 (TAB 6)  

2022

  • added: Board rotates as appropriate
  • fixed: When two humans are playing, and the ai is set to white but turned off, the ai anyway takes the first move for white

2020

16th March

  • added: AI4/5 takes #movelist into account for value
  • adjusted: AI5 acceptable range up from 2 to 15
  • adjusted: pawn worth table to make b3/b6/g3/g6 less valuable

12th March

  • fixed: analyze function does not calculate a zero-sum value
  • fixed: castling can occur with an enemy bishop or knight
  • fixed: castling with an enemy bishop or knight resurrects own rook
  • improved: when ai starts as white, the board is now drawn before it starts thinking
  • improved (arguably): all worth tables
  • added: ai_four and ai_five
  • fixed (possibly): ai_two/ai_three early random pawn moves lead to loss of pieces

6th February

  • commented out: unused per and low variables
  • improved: speed of move reply when there is only 1 move available
  • improved: speed improvement for ai_two (and ai_three) via only calling flip() when stat(1) is above a certain level

20th February

  • fixed: computer crashes when thinking about promoting a pawn
  • improved: worth values for kings and pawns
  • fixed: erroneous value for worth for rooks (worth more than they should have been by a factor of about 20 on some squares)
  • fixed: white player (human or AI) uses worth table for black positions
  • fixed: next game after human plays black causes SWEEZEchess to freeze irrecoverably
  • improved: title screen clarity for which pieces AI is controlling
  • improved: menu clarity for which pieces AI will control
  • fixed: option to resign overwritten in menu
  • fixed: occasional unrepeatable crashes

Detail of fixes and improvements

2022

31st March

description: when ai_on is set to false but the ai player is set to have the first turn, the ai takes the first turn as white instead of letting the human playing with the white pieces do so

function: _update

fix: added 'and ai_on' into the following line of code

 if (btnp(🅾️) and state == 4 and not gong and ai_on and startturn == 1) ai_move(turn*2-1)

description: board cannot be rotated

functions: drawboard, drawpiece, _update (tab 0)

fix:

  • separated call to drawpiece from the drawing of squares in drawboard, making it easier to perform a board rotation in drawpiece
  • in _update, added inverted controls for pieces, using an if statement to choose which mode of control is used:
    if (ai_on and startturn==0) or (not ai_on and turn==0)then

2020

12th March

description: analyze function does not return a zero sum value; this can be shown by starting a new game, pressing escape, typing in analyze(1) then ?value which will print the value as 59 when it should return the value as 0 at the onset of the game.
function: analyze (tab 5)
rewrote using adapted code from AI4/5, see cart

description: Kings can "castle" with an enemy bishop or knight after it has captured a rook, and the rook is resurrected in its place, and this should not be possible.
function: turndo (tab 1)
fix: to prevent resurrection of the castle, the piece that moves is now whatever piece is on the square preventing resurrection, with code such as:

board[yd][4] = board[yd][1]

instead of:

board[yd][4] = 2+turn*10

A more tentative fix for being able to castle with an enemy piece has been added (also in turndo); with the intention to change the value in the table crook when a piece finishes its move on a rook's starting square:

  if(yd==8 and xd==1)crook[1]=0
  if(yd==8 and xd==8)crook[2]=0
  if(yd==1 and xd==1)crook[3]=0
  if(yd==1 and xd==8)crook[4]=0

description: when the AI starts as white, there are a few seconds for AI2/3, or more than a few seconds with AI4/5, as it considers its move, when the titlescreen remains up as if frozen
function: _draw
took the code for drawing the board out into its own function drawboard
function: deeper
added a call to drawboard

description: Early pawn moves can take precedence over sensible moves such as moving a queen or knight away from where it can be taken.
function: ai_two (tab 6)
fix: added an extra condition (high > ai_value-15 ) into an if statement

 if high > ai_value-15 and pawncount > 5 and not gopnyk and rnd(2) > 0.2+turn_counter/3 then

20th February

2 speed improvements

description: flip() occurs in every iteration of the loop, even when only a little of the CPU resource has been used for that frame
function: modifgister
improvement: use stat(1) to see how much of the CPU is left available for that frame, and only flip() when it starts to get high

   if(stat(1)>.85)flip()

description: ai thinks about moves when there is only 1 move available
function: ai_two
improvement: return from ai_two with an early check for list size of 1

   if #list==1 then
    split(list[1])
    return
   end

Also: commented out unused low and per variables, but left them in the program for future use.

4th February

tab 6
bug: occasional unrepeatable crashes
reason (rare) when the random number generated is 0, using ceil will leave it as 0, which means items in a 1-based list won't be selected
functions: ai_one ai_zero ai_two
fix:changed 5 uses of ceil to 1+flr

tab 0
bug: no option to resign
bug: start turn duplicates in menu
reason: some numbers overwrote others
function: _init
fix: numbered menu items from 1-5 and put "start turn" as menu item 3 to align with toggle_turn

clarity changes connected to above:
functions: _init toggle_turn
reworded item 3 as "set ai to [ ]"
function: _draw
reworded print to: "ai controls black/white pieces"

bug: when human controls the black pieces if human loses, then starts another game, the computer moves a black piece as the opening move of the game then the game freezes.
reason: on initial run startturn is 0, so turn is 0. on subsequent runs, startturn can be 1 or 0, so turn becomes 1 or 0, which leads to the bug.
function: setup
changed: turn = startturn
to: turn = 0

tab 5
bug: white plays with black values
reason: see "ya =" in the if lines in the following code:

   if ga > 10 then
    ga -= 10
    if (wdir == -1) ya = 9-y else ya = y
    value += worth[ga][ya][alx]*wdir
   elseif ga > 0 and ga < 10 then
    if (wdir == 1) ya = 9-y else ya = y
    value -= worth[ga][ya][alx]*wdir
   end

function: analyze
recoded one line:

    if (wdir == 1) ya = y else ya = 9-y

tab 5
?bug: computer likes moving rooks to the b file because they are worth 999 points there
gameplay strength: other 'worth' values seem slightly out and could be improved
function: setupworths
changed data to alternative worths so it will hopefully play slightly better

tab 2
bug: when the computer has a pawn about to promote, it crashes when it thinks.
function: castpawn
added: a local variable to put into the first if condition to stop the computer thinking about promoting
code added/changed:

 local inbounds=s_y+pdir>0 and s_y+pdir<9
    if inbounds and board[s_y+pdir][s_x] == 0 then

P#87297 2021-02-06 17:24 ( Edited 2022-08-31 14:42)
2

@remcode

wow! timely or not, i'm happy to see any interaction with this!
i'm astonished my code was legible enough to reverse engineer and fix!

i was planning on getting back to this at some point to fix a few bugs, as there are a few that're still extant (primarily one which, considering you seem to be a better chess player than i, i'm not surprised you haven't come across: if your rook gets taken before your king has moved, you can still castle with it; the castling will take place as if the piece that took the rook was the rook and it'll basically necromancy it back into existence. i know how to fix this, just to check if the rook is actually alive for whether it's legal, but it's pretty silly i missed that.)

regardless of the still living bugs, thanks for making the fix!
maybe this'll be the kick i need to start working on it again lmao

P#87302 2021-02-06 17:36 ( Edited 2021-02-06 17:37)

@MEINsweeze

re: legible - yes ... it took a while to work out enough of how it worked to fix some of these (a few local variables, and returns from functions might have made it easier for me) :-)

I didn't mention it above but I think while I was trying things out to fix the problem with white playing to lose, I may have swapped the 1 and -1 in the calls to analyze(1) and analyze(-1) at the tops of tabs 1 and 6 around. This is purely accidental - I have to admit I'm not entirely sure what difference it makes, or if it makes a difference depending which colour pieces the AI is playing. This might be a change you'll need to playtest to make sure I haven't made the gameplay worse. [Edit: I am sure now I put them back the right way round, and it makes a considerable difference.] :)

Good luck with any future fixes you make to it!

P#87303 2021-02-06 17:54 ( Edited 2021-02-21 13:28)
1

@remcode

thank you! i'll try to fix it, and i'm gonna be honest, i didn't know about local variables and returns at all when i made this! the language i'm used to programming in, turing, is very similar to lua except locals aren't a thing; when i learned lua, i never bothered to google how to do local variables since i was completely unaware that they were even a thing. as a result, i use an abundance of variables, and that gets tedious, so i just name neatly every variable whatever mouth sound i make, maybe plus an x or y if it's revelant lmao

as for returns, that's more of my bad habits; i use so many variables that my first instinct is to just save the result of a function as yet another global variable

EDIT: oh god, turing has locals too.. i just never learned about them. :(

P#87305 2021-02-06 18:14 ( Edited 2021-08-18 02:54)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-03-29 00:25:27 | 0.063s | Q:29