Giter VIP home page Giter VIP logo

4ku's Introduction

4ku

A chess engine written in C++ designed to fit into 4,096 bytes. There are two versions of the engine: 4ku, and 4ku-mini.

  • 4ku-mini uses source code that is stripped, minified, compressed, and then appended to the launch script. When run, the launch script compiles the source code to a temporary executable and then runs it.

  • 4ku is a normal compile of the same source code. It is not stripped so retains support for UCI setoption, info strings, and perhaps other quality of life improvements.

4ku and 4ku-mini should be identical in terms of their play, but 4ku's info strings mean it is probably slightly slower and slightly weaker. Despite this, 4ku's ease of use and cross-platform compatibility means it should probably be favoured for use in any circumstance other than being limited to 4,096 bytes.


Build Instructions

4ku can be built normally, but it won't fit into the size restriction:

git clone https://github.com/kz04px/4ku
mkdir 4ku/build
cd 4ku/build
cmake ..
cmake --build .

To build 4ku-mini on Linux, run build-mini.sh located in the root directory:

bash build-mini.sh

4ku-mini is currently unavailable on Windows.


4ku-mini Size

3,937 bytes

Requirements

4ku only needs a C++ compiler to be built and should work across platforms. 4ku-mini has the following additional requirements:

  • python3
  • lzma

The build script, launch scripts, and compression tool (lzma) are all specific to Linux and would need replacing for 4ku to run on Windows. The code itself should be portable.


Minification

The minifier requires Python 3 to run. It's fragile and will not handle arbitrary C++ code. The minifier is not a general-purpose solution and is not guaranteed to work on non-4ku code. If better solutions are found in the future it can be replaced.

Removed:

  • Whitespace
  • Single line comments
  • Block comments
  • const
  • noexcept
  • Attributes: nodiscard, maybe_unused
  • Calls to assert() and static_assert()
  • enums

UCI Support

4ku-mini has limited UCI support to save space:

  • The first line of input after startup will be interpreted as uci even if it isn't.
  • stop is unsupported.
  • position fen [fen] moves [moves] is not supported. position startpos moves [moves] must be used instead.
  • go only supports wtime and btime, in that order.
  • setoption is not supported. Relevant variables are hard coded.

4ku has additional support for:

  • setoption
  • position fen [fen] moves [moves]
  • info strings

Thanks

4ku's People

Contributors

adam-kulju avatar alex2262 avatar cglemon avatar cj5716 avatar crippa1337 avatar diazepawn avatar dsmsgms avatar gcp avatar gediminasmasaitis avatar kz04px avatar lambda0x00 avatar mcthouacbb avatar mhouppin avatar pgg106 avatar unaiic avatar uwuplant avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

4ku's Issues

save byte by deleting some lines

Hi
you can save some byte if you delete these lines (in the main function) after the line
goto search_start;

        cin >> word >> time_left;
        if (pos.flipped)
           cin >> word >> time_left;

these lines are never used.

4KU2 rated 2000 !?

Congrats with the newer 4KU2 ! Lately i compiled and tested the version "18", where you implemented some eval functions, and it playes very well against 2000 rated engines .. i let 4KU2 play Black in several 10 min + 3 sec games, using CuteChess on Linux (no books, no pondering, 128 Mb Hash, 2 Threads). Here's the ZIPped PGN 4KU2_games.zip

1827  Apollo v1.2.1                0-1
1868  Rustic Alpha v3.0.2          0-1
1882  Princhess v0.8.0             0-1
1888  Surprise v4.3                0-1
1907  Claudia v0.5.1               0-1
1918  Presbyter v1.3.0             0-1
1925  Heracles v0.6.16             0-1
1932  Quokka v2.1                  0-1
1939  Dabbaba v6.52                1-0
1950  Monik v2.2.7                 0-1
1954  Wowl v1.3.8                  0-1
1968  ZetaDVA 0310               1/2-1/2
1990  ALChess v1.84                0-1
1990  Sapeli v2.1                  0-1
1992  Fatalii v0.4.0 alpha         0-1
2008  Leonidas v8.3              1/2-1/2
2026  Cupcake v1.1c                1-0
2027  ArabianKnight v1.55          0-1
2032  Poor Little Pinkus (PLP)     1-0
2033  Clunk_v1.2                 1/2-1/2

4KU2 plays good chess - i see that, being a club player .. it develops naturally, it prefers to let pieces being captured, it exchanges pieces only when needed .. it seems to have plans to place the pieces and use the pawns, in a logical harmonious way .. it can sacrifice pawns when needed .. it may guard pieces by attacking those of the opponent .. also the endgame is played rather well, in most cases - in a few games 4KU2 was lost (-3 / -4) according to the opponent eval, but managed to win.

so, compared to many other well-known (?) amateur engines 4KU2 performs very well, i estimate its rating around 2000.

Note the few Black games with the Berlin Defence, a variation of the Spanish game 'Ruy Lopez' :

  1. e4 e5 2. Nf3 Nc6 3. Bb5 Nf6 4. O-O Bd6 5. d4 Nxd4 6. Nxd4 a6

WeirdBerlinVariation

the moves 4...Bd6 and also 4KU2's solution 6...a6 are weird, but seem playable .. in general 4KU2 plays the openings rather well and consistently.

python subprocess : catch both stdout and stderr ?

the following code line works with most UCI engines :

p = Popen( [ "sh", "my/path/to/engine/run.sh" ], stdout=PIPE, stdin=PIPE, universal_newlines=True )

those engines only seem to use stderr for their starting "uci settings output", their info lines are all in stdout, which i can catch and filter .. for my script i don't need the stderr info .. however, 4KU seems to (also) use stderr for its output, am i right? .. not sure .. i tried several coding solutions and examples i found on StackOverflow, but i can't create a working PIPE construction to catch both stderr and stdout .. can you help me with this ?

endgame skills

these days i keep testing your (newest) 4KU2 version(s) .. it seems 4KU2 plays the endgame rather well, but i saw some severe mistakes .. here are two examples, 4KU2 played Black in both games.

this is from the game against Little Wing v0.7.0 (2169).
FEN: 8/6k1/5pp1/2Q5/4K1Pp/P4Pq1/8/8 b - - 0 45
both teams have equal material, it's a difficult endgame where both players only have a Queen and a few pawns .. here accurate moves are required : win, loss or draw are all possible .. Black has only 1 move which wins : ...Qe1+. Your 4KU2 finds it, but does not win the game, it ended in a draw.

littlewing-vs-4ku2_draw_but_could_win

this is from the game against Dabbaba v6.52 (1939).
FEN: 8/8/8/8/2k2p2/5P1p/4K2P/8 b - - 0 65
both teams have equal material, it's a drawn endgame but 4KU2 tries to win .. here Black must not let the White King come to d3, but this is what happened and 4KU2 lost ..

dabbaba-vs-4ku2_lost_but_could_draw

Error upon build in function clock_gettime()

Environment: macOS 13.1, g++ 12.2.0

Commands:

sh build-mini.sh
./build/4ku-mini

Error message:

<stdin>: In function 'int64_t a()':
<stdin>:11:82: error: invalid conversion from 'int' to 'clockid_t' [-fpermissive]
In file included from /opt/homebrew/Cellar/gcc/12.2.0/include/c++/12/ctime:42,
                 from <stdin>:5:
/Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk/usr/include/time.h:178:29: note:   initializing argument 1 of 'int clock_gettime(clockid_t, timespec*)'
  178 | int clock_gettime(clockid_t __clock_id, struct timespec *__tp);
      |                   ~~~~~~~~~~^~~~~~~~~~
./build/4ku-mini: line 5: /var/folders/5m/v_lfr66s1mg7l9qhv3mjfkfc0000gn/T/tmp.4DH2RQex: Undefined error: 0

Cause of error occurs in src/main-mini.cpp, line 11:

using namespace std;int b=1<<15;int c=1<<16;int64_t a(){timespec d;clock_gettime(6,&d);return d.tv_sec*1000+d.tv_nsec/1000000;}

In turn this is caused by main.cpp line 52:

clock_gettime(CLOCK_MONOTONIC, &t);

handle equal eval with more time ?

i was wondering : while testing some UCI engines with some interesting position, i stumbled upon the fact that 2 or more moves might have equal eval (cp) .. i'm using MultiPV for that .. i'm not asking for MPV in 4KU here, but does it have some table alike while calculating ? Sometimes, early in a game, 4KU thinks for 2 minutes while doing about 30 sec most moves : did the top variations have such equal eval and 4KU took more time / depth to distinguish ?

about Hash usage

i'm still testing the (newest) 4KU2 versions by compiling them and let them play with several engines in CuteChess (on Linux). I gathered a lot of (weaker) open source engines and i try to match opponents with similar rating - my current guess of 4KU2 rating is 2100. These are all seperate games (10 min + 3 sec bonus), i mean i do no tournament in CuteChess.

i like your minimalistic approach .. although i'm not a C++ programmer (but mainly Python and JS), your code seems very well written and is just one small cpp file! The UCI subset which you use is enough for playing in CuteChess (or similar), it understands just "position startpos moves [...]" and "go wtime 603000 btime 603000 winc 3000 binc 3000" or similar.

i guess 4KU2 is not for playing in terminal, but it can be done. Looking at the games 4KU2 plays in CuteChess, i see it has a natural way of developing the pieces and pawns. Using the same time settings in all games, 4KU2 seems rather consistent in its openings, only a few times it plays another variation (when the opponent played known opening moves). I guess in such case both moves have almost equal eval, maybe the hash 'decided' the "best move" ? To test a position with different settings, i used terminal by entering the proper (large) move list to reach that position .. i had some odd results, maybe hash is involved ? Then how ? Could such tests help your development ? Can you reproduce them anyway ? - myself i didn't redo the same terminal sessions to see if they would lead to the exact same results .. btw. i use a rather modern avx2 notebook and Xubuntu 22.04

i want to show the terminal output concerning this position :

4KU2-pos-after-e4e5

UCI : position startpos moves e2e4 b8c6 d2d4 d7d5 e4e5
FEN : r1bqkbnr/ppp1pppp/2n5/3pP3/3P4/8/PPP2PPP/RNBQKBNR b KQkq - 0 3

This is a well known position in chess theory. Most positions can be reached by altered move order, but normally the game went like this :

  1. e4 Nc6

This opening is called the Nimzowitch Defence. 4KU2 also prefers e4 on move 1, but with Black it would answer the good standard e7e5 :

ucinewgame
id name 4ku2
id author kz04px
uciok
position startpos moves e2e4                    
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove e7e5

Our game continues with :
2. d4 d5

4KU2 prefers Nf3 on move 2, but after 2.d4 it also prefers to answer d5 with Black :

position startpos moves e2e4 b8c6               
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove g1f3
position startpos moves e2e4 b8c6 d2d4          
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove d7d5

Our game continues with :
3. e5 ...

Here 4KU2 prefers not to advance the e-pawn, it likes to keep the initiative by e4xd5 :

position startpos moves e2e4 b8c6 d2d4 d7d5     
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove e4d5
position startpos moves e2e4 b8c6 d2d4 d7d5 e4e5
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove f7f6

We reached the position of the diagram. Here 4KU2 seems to prefer 3...f6 when given 10 minutes (per game), but when i let 4KU2 think again (same hash?) it finds other moves too ?! :

go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove f7f6
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove f7f6
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove f7f6
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove f7f6
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove f7f6
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove c8f5
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove c8f5
quit

And what about my test below (after restarting 4KU2 in terminal) .. same position, other best move :
(Note: the move Bd7 was once picked also)

ucinewgame
id name 4ku2
id author kz04px
uciok
position startpos moves e2e4 b8c6 d2d4 d7d5 e4e5
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove f7f6
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove c8d7
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove c8f5
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove c8f5
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove c8f5
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove c8f5
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove c8f5
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove c8f5
go wtime 603000 btime 603000 winc 3000 binc 3000
bestmove c8f5
quit

And this test, using a longer time :

ucinewgame
id name 4ku2
id author kz04px
uciok
position startpos moves e2e4 b8c6 d2d4 d7d5 e4e5
go wtime 1603000 btime 1603000 winc 3000 binc 3000
bestmove f7f6
go wtime 1603000 btime 1603000 winc 3000 binc 3000
bestmove f7f6
go wtime 1603000 btime 1603000 winc 3000 binc 3000
bestmove c8f5
go wtime 1603000 btime 1603000 winc 3000 binc 3000
bestmove c8f5
go wtime 1603000 btime 1603000 winc 3000 binc 3000
bestmove c8f5
go wtime 1603000 btime 1603000 winc 3000 binc 3000
bestmove f7f6
go wtime 1603000 btime 1603000 winc 3000 binc 3000
bestmove c8f5
go wtime 1603000 btime 1603000 winc 3000 binc 3000
bestmove f7f6
quit

Here, the two moves Bf5 and f6 (which indeed are best according to SF) are both 'best move' after some time, but why do they toggle ?

PV printing can contain illegal moves

Warning: Illegal PV move e7g7 from 4ku (0)

That's because there's no move legality check for the move from the hashtable, and especially in multi-thread scenarios stomping is possible.

Thread number and node count

Hi,

I think there is a bug with the node count and thread > 1.
With monitoring the thread number is here but the node count is same with one or more threads used.
do you have the same issue ?

Regards

4KU fails mate

i just encountered a strange playing by 4KU : it fails to mate when clearly winning (eval is +296 or so) : it had Queen and kNight to mate a lone opponent King but lost due to the 50-move rule .. i never saw that with previous versions .. i tried to reproduce this by re-entering such position (by the CuteChess opening book feature) but then 4KU plays well : it mates !? Here is a ZIPped pgn 4KU-fails-mate.zip the first game is the original, the other 2 games start after move 74 to setup such winning endgame position.

why calculate when only 1 legal move ?

many chess engines have this issue .. it's unclear to me why this happens : in such case an engine often thinks for many seconds, which could easily be saved .. btw. i encounter this with 10m+3s games, the only ones i do at this moment.

could there be an easy fix, not taking much byte space, creating this logical and efficient improvement ?

smart hard coded end game pattern ?

i was wondering : some (weaker) engines can not do mate with only B & N, they don't know the concerning 'pattern' and often lose by 50 moves .. maybe 4KU can solve it ? Until now i did not test that .. without having a #-piece-end-game-TB, is some hard coded KKBN pattern allowed ? Does such even exist in chess programming ?

code : how is start position defined ?

in your README you state:

Both "position fen [fen]" and "position startpos" are ignored.

indeed, if you want a very simple engine which can only play from the startposition, you don't need those .. but when i look into the source of main.cpp (although i'm not a C++ programmer) i discover no definition of the start position .. i wanted to adjust the code a little to implement some position fen [..] command - just for learning. Your code seems readable though ..

size matters

Congrats with the new v2.0! The Changelog is impressive .. you guys certainly know how to build a chess engine, using all known techniques optimally, to even get all code into 4 Kb !! Regarding that size : why is the released (linux) asset 2.9 Mb ? Is it because that version will optimally run on ALL linux distros ? My compiled version is only 59.7 Kb !

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.