欢迎光临
我们一直在努力

世界上最小的国际象棋程序

本周之前,世界上最小的国际象棋程序是1983年发布的1K ZX Chess,大小仅为672字节,它包含了大部分国际象棋规则。为了证明技巧仍然有其价值,Red Sector公司的开发者从去年开始对这一尘封32年的记录发起了挑战
他们最终创造出了只有487字节的计算机国际象棋程序BootChess(源代码),支持 Linux、MS-Dos、WindowsFreeBSDMacOSX Intel。[codee]
|=———————————————————————–=|
|=—————————-=[ BootChess ]=—————————-=|
|=———————————————————————–=|
|=————————=[ by Baudsurfer/rsi ]=————————=|
|=———————————————————————–=|

1 – Introduction
1.1 – Why make tiny programs ?
1.2 – Proving know-how remains valued

2 – The 33 year-old record contender
2.1 – David Horne – the king
2.2 – False claims abound

3 – Understanding sizecoding hurdles
3.1 – The forbidden approximations
3.2 – The initial environment
3.3 – Some ill-defined standards

4 – The heart of the difficulty
4.1 – What is not difficult per se
4.2 – Twelve different pawn moves ?
4.3 – What you get – what you don’t

5 – The BootChess program dissected
5.1 – The TaxiMax ai used
5.2 – The data constants used
5.3 – The program flow explained
5.4 – Analysing a random snippet

6 – The Source code
6.1 – FAQ and remaining questions
6.2 – BootChess.asm
6.3 – Encoded base64 floppy image

7 – Thanks/Greets
8 – References

|=–[ 1 – Introduction ]=————————————————=|

|=–[ 1.1 – Why make tiny programs ?

Last year in 2014, Red Sector Inc. released many sizetros in and out of
the demoscene demoparty loop. Red Sector Inc. is an old scene group from
1985 founded by me and 4 other guys, celebrating their 30th anniversary
this year. Sizetros were also known as cracktros before because once you
cracked a game there usually was very little space left on the floppy
disk to put something else and BBS WHQ sysops were always found of the
smallest versions around to courrier when bandwith was still an issue.
Nowadays and in the demoscene, these are known as sizetros (a contraction
of size and intro) : 64 bytes, 128 bytes, 256 bytes, 512 bytes. Above
512 bytes it is usually admitted by oldskool standards that one has
enough space to set up a software library based framework instead of pure
hardware (Directx, OpenGL, WebGL, SDL, shaders, OS files) and/or call
external professional packing programs (aPACK, UPX or GNUZip) or internal
OS packing libs (.png Deflate, .cab lzx dropping) : the final binary
footprint is hence misleading not lending itself to any fair comparisons.
So last year I presented 3 sizetros to 2 demoparty competitions : 2 of
them won respectively the Outline demoparty 128-byte competition in the
Netherlands and the Function demoparty 256-byte competition in Hungary.
The last one got only second place at the former demoparty but became a
phenomenon because some person called FinalPatch dissected its provided
and commented sourcecode for the Reddit website users who had trouble
understanding its innerworkings. His article was entitled “dissecting
the 128 byte raycaster” and soon became referenced by the whole interweb
including but not limited to 4chan, hackernews, facebook etc. A friend
ascii artist Cleaner informed me of this and I proceeded to briefly read
most of the comments. Out of the 800 or so references to that article, one
of the comments thread from hackernews explains well the philosophy and
the mindset of sizecoding supporters.

|=–[ 1.2 – Proving know-how remains valued

>leorocky 206 days ago | link
These kinds of ray casters are really cool, but at this point in time I
don’t care about how small the code base is, just make a really cool ray
caster in JavaScript and don’t worry about how big the code is (within
reasonable limits)! I just want an awesome JavaScript ray caster library
to be honest. 🙂
—-
>dguaraglia 206 days ago | link
… and that’s exactly why you don’t get why this guys are trying to
achieve. This guys are counter-culture. They are trying to write the
smallest functional ray caster/sound engine, you name it. They target
maximum functionality with minimal bloat. Coming to a post like this to
say you want a ray caster in JS ‘no matter how big it is’ is very
misguided; the equivalent of going to a Tesla dealership (assuming such a
thing existed) and telling the dealer ‘well, this is all amazing
technology and stuff, but what I really want is a Humvee with two
cupholders on each seat, 5 TVs and a driver, I don’t care if I need an
atomic power plant to keep it running’. It’s all fine and dandy here, HN
is an open group after all. But don’t go with that kind of comment to a
‘scene’ forum or they’ll chew your head off 🙂
—-
>jameshart 206 days ago | link
I’m not sure you appreciate quite how small 128 bytes is; for comparison,
your entire comment is 305 bytes. This one’s just 128.
>cpayne 206 days ago | link
I think that is the best argument you could make!
—-
>RBerenguel 206 days ago | link
There was one posted a few weeks ago. 128 bytes is incredibly impressive.
You may not care, the world may not care, but reducing code so much is a
work of art
—-
>jzzskijj 206 days ago | link
A interactive raycaster in 128 bytes and you “don’t care”. No matter what
year it is, it is a feat that really says “BEAT DIS”.
—-
>na85 206 days ago | link
What a typical HN response. Do everything in JavaScript.
—-
>leorocky 206 days ago | link
Looks like this comment of mine was irrevocably wrong and completely
irredeemable. No apology is possible and I’m sorry for my very existence.
Go social voting sites! Go internet culture! Yay.
—–
>bunderbunder 206 days ago | link
A harsh response, yes. But frankly, what you said was harsh. An
interactive raycaster in an executable an order of magnitude smaller than
most networks’ minimum transmission unit is an incredible accomplishment,
and your response is to essentially drop trou and take a dump on it
because teh w3b 4evaz.
—–
>leorocky 206 days ago | link
What I said wasn’t harsh, it just wasn’t apparently enough genuflection
and awe to appease the people that happened to be interested enough to
check the comments on this link. I care about as much as I did yesterday
which is not nothing. You and everyone else who demands more will just
have to deal with it.
—–
>fl0wenol 205 days ago | link
No; if you don’t appreciate something because you suspect you don’t
understand or lack context (as you expressed), you should just keep your
mouth shut lest you sound like an idiot.
—–
fyolnish 206 days ago | link
To be perfectly honest, you should be sorry.
—–

Fyolnish you are kindly forgiven but don’t think your attitude has nothing
to do with Phrack’s article “The Fall of Hacker Groups” [e] if you take
care to read between the lines.

But more to the point one comment on facebook really caught my attention :

“At last a contender for 1K ZX Chess”.

|=–[ 2 – The 33 year-old record contender ]=—————————-=|

|=–[ 2.1 – David Horne – the king

Wikipedia”s mildly error-prone entry for 1k ZX Chess [5] states simply :

“1K ZX Chess is a chess-playing computer program for the unexpanded
Sinclair ZX81, created by programmer David Horne.[1] The game implements
most chess rules (castling, queening, and en passant capture are missing),
artificial intelligence, and a user interface. The code uses only 672
bytes of RAM, making this the smallest computer implementation of chess
on any platform.” Along with the faulty release date of 1983 when it was
in fact first published in Your Computer magazine when Artic Computing
started selling it in 1982 actually.

|=–[ 2.2 – False claims abound

I also did some research and nobody had ever beat this small size despite
numerous false and misleading claims last years such as mistaking size of
memory environment used with the binary footprint, counting total number
of sourcecode C characters (Toledo nanochess 18th IOCCC) or interpreted
javascript characters (Tiny Chess JS1k 2010), falsely entitling bigger
sized programs “The world’s smallest chess program” on the Google web,or
affirming senselessly the brainfart “int main(void){puts(“I resign”);} is
a “legal [playable ?] FIDE chess program” (sic).

In fact some person even attempted [f] to convert David Horne’s code from
Zilog to x86 for Intel had supposedly published a backward compatibility
translator tool called conv86 [0] to no avail and ended coding a virtual
machine around original 33 year-old code. So despite 2 honorable mentions
for 2 other 1k chess games named Microchess [2] from Peter R. Jennings on
the 6502 microprocessor in 1976 and named Tiny Chess 86 [3] for the 80/86
from Jan Kuipers in 1981, nobody seems to have ever beaten David Horne’s
tight 1 kilobyte code. In short, David Horne was already what one calls a
hacker in the original sense when he programmed 1K ZX Chess and if it was
that easy to beat his work somebody would have done it before, for real.

The plan was to take what has been referenced in the past and notably by
kuro5hin as “the greatest program ever written” [c]… And half the size,
making it available to any IBM PC, knowing any porting attempt was doomed
to fail. BootChess took 3 months to write (1 to 3 hours per day at night).
Since October 24th 2014, 239 intermediate different versions were saved.

|=–[ 3 – Understanding sizecoding hurdles ]=—————————-=|

While similarities between run-time graphical optimizations and BootChess
exercise were evoqued herabove, notable exceptions abound for formal rule
gameplay such as the game of chess governed by the FIDE organization [b].

|=–[ 3.1 – The forbidden approximations

In effect, foremost there exists a tacit rule in the demoscene stating a
minor visual discrepancy or code originated non-stylized “glitch” remains
acceptable whether present throughout whole presentation or brief moment,
particularly for sizeprods. For example, in standard VGA lowres mode 13h,
or 320×200 in 256 colors, there are 64000 bytes on the physical screen :
if a few of these byte-sized picture elements (pixels) flicker on the
screen due to significantly size-reducing the code through some elaborate
or elegant runtime algorithm, the demoparty voting community will accept
to turn a blind eye to such practices – not so in a case-based game where
even if an algorithm works 95% of the time it must me discarded promptly.
The very smartest hacks are thus strictly forbidden. Any side-effect from
instruction A will thus break iso-functionality and be considered as game
code rule regression instead of arty path choice affecting instruction B.

|=–[ 3.2 – The initial environment

Secondly there does not exist an official discrete starting assumed cpu
context where the state of each register is properly defined. In graphic
sizetros and its prefered platform DOS the starting .com binary format
launching register assumptions remain precisely defined in xp sp3 [6] :
flags=3202h di=fffeh si=100h bp=091eh sp=fffeh bx=0 dx=6bch cx=0ffh ax=0
Hence, knowing that state lends itself to drastic optimizing such as seen
in all-time top5 [4] 64-byte textured raymarching intro named “Orbit” [7]

org 100h ; COM after PSP original version for comparaison
mov al,93h ; we know ah to be zero
int 10h
les bp,[bx] ; we know bx to be zero

Where taking advantage of the assumed known nul bx value at start enables
after the last line es=9fffh so that assumed es:di represent the pointer
to the start vga video memory 0a000h:0000 when adding di para+2=18 bytes.

|=–[ 3.3 – Some ill-defined standards

In a bootloader or BIOS to MBR interface you can only count on cs:ip to
equal 0:7c00h (even there there are cases of wrong 7c0:000h) and the dl
value representing the boot unit (in our floppy/superfloppy case dl=0).
As OSDev states “the values in all the other registers, and in most of
memory, are undefined” [9] even if one expects the last call to be the
read sector(s) into memory function 2 from POST’s interrupt 13h [8].

org 7c00h ; BIN after POST yielding a 3-byte overhead
mov aX,93h ; +1 byte we don’t know ah to be zero
int 10h
xor bx,bx ; +2 bytes …
les bp,[bx] ; … we don’t know bx to be zero

Also a VBR need not explicitely bear the aa55h magic number as defined by
legacy IBM PC/AT:

“[…] while for floppies and superfloppies it is enough to start
with a byte greater or equal to 06h and the first nine words not
to contain the same value, before the boot sector is accepted as
valid.” [a]

But since not all bios makers have been known to be keen in enforcing the
retro-compatibility with original version of code one sadly cannot always
take advantage of this initial “conforming first 19 bytes” bootable VBR
algorythm as devised by IBM to gain 2 bytes extra.

For this reason, BootChess possesses two main precompiling switches. The
first switch named “x86” is to make a floppy boot binary image either for
a real PC machine or an emulator if set to “1”. Setting the “x86” swich is
for whom are wanting to make a legacy Dos .com binary which will execute
natively up to Windows7-32bit version (or DOSbox emulator). The second
switch “saf”‘s purpose is to circumvent the above mentionned discrepancies
on lesser-known exotic BIOS firmware (mainly enforce 0:7c00h startup and
still insert boot signature on vbr) when set to “1”. When set to “0” the
size freed up by these fail-safe mechanisms enables extra implementation
of pawn promotion to queen.

|=–[ 4 – The heart of the difficulty ]=———————————=|

|=–[ 4.1 – What is not difficult per se

It is not so difficult to logically program a chess game. The same could
be said about system programming a boot sector or about programming a
demoscene sizetro. On the contrary it is a challenge to all 3 at once and
the result sourcecode won’t do it justice – as a laborious trial-and-error
process it is neither pretty nor magic in any sense.

|=–[ 4.2 – Twelve different pawn moves ?

In computer chess programming, the pawn movement is often referred to as
a “special case”. This euphemism applies to what is considered the dullest
chess piece on the board to mediocre chess players like the author : pawns
are governed by no less than 16 different conditions (which of first 14
are implemented in BootChess) :

1.simple square unidirection 1 legal non-capturing vertically ;
2.simple square unidirection 2 legal non-capturing vertically ;
3.simple square unidirection 1 illegal capturing vertically ;
4.simple square unidirection 2 illegal capturing vertically ;
5.double square unidirection 1 legal non-capturing vertically ;
6.double square unidirection 2 legal non-capturing vertically ;
7.double square unidirection 1 illegal capturing vertically ;
8.double square unidirection 2 illegal capturing vertically ;
9.simple square unidirection 1 legal capturing diagonally ;
10.simple square unidirection 2 legal capturing diagonally ;
11.simple square unidirection 1 illegal non-capturing diagonally ;
12.simple square unidirection 2 illegal non-capturing diagonally ;
13.unidirection extreme rank promotion 1 ;
14.unidirection extreme rank promotion 2 ;
15.”en passant” unidirection capture 1 ;
16.”en passant” unidirection capture 2 ;

nb : in chess the columns are named “files” and the rows called “ranks”

In light of what was written in 4.1, and because a common processing path
has to be devised for all chess pieces because of size constraints, which
includes the five others chess piece types, it is not feasible to simply
have a classic exhaustive swithcase scenario to manage the pawn movements.
Instead the pawn movements must integrate themselves seemingly within the
default case program flow applied to the other pieces however inelegantly.

|=–[ 4.3 – What you get – what you don’t

+you get a graphic text representation of chess board and use input ;
+you get a bootsector sized (512 bytes) with a playable chess game ;
+you get a x86 bios hardware only bootstrap (no software dependancies) ;
+you get all main legal moves including douvle square pawn start ;
+you get pawn promotion to queen (contrary to 1k ZX Chess) ;
+you get some cpu ai called taxiMax > minMax half-ply ;
+you get a hardcoded Spanish white pieces opening ;
+you get a Linux, Windows, OSX, BSD386 machine compatible sourcecode ;
+you get the smallest computer chess game ever programmed yet.

-you don’t get fancy graphics (a 16×16 binary sprite is 32 bytes) ;
-you don’t get under-promotion ;
-you don’t get “en passant” pawn capture ;
-you don’t get castling (queen or king side) ;
-you don’t get the 3-repetition rule ;
-you don’t get the 50-moves draw rule ;
-you don’t get opening and closing books ;
-you don’t get one or more minMax/negaMax full plies for ai.

If you have read everything up to here, you will have realized we
have not taken into account any partition table – the reasons we would
rather have a floppy boot record rather than a hard disk boot record,
are :
+more people are willing to try-out inscribing a floppy then their hdd ;
+we get 16 more bytes free (or 64 bytes free in case of multiboot) ;
+whether it’s BootChess or 1K ZX Chess, both are fun temporary gadgets.

|=–[ 5 – The BootChess program dissected ]=—————————-=|

|=–[ 5.1 – The TaxiMax ai used

Usually the heart of any midly advanced computer chess games includes a
MinMax function (or its unique call merging sister function NegaMax)
called recursively evaluating both sides’ possible moves and trying to
minimize loss whilst maximizing captures : each evaluation pair rundown
is called a “ply” in chess jargon. It can take Kasparov some 27 moves to
battle a 3-ply and the champion programs competing with grandmasters
usually have at least 5-ply. In the case of BootChess there is alas not
enough space (512 bytes binary program size and 640 bytes RAM execution
environment) for such level of sophistication. It uses a variant : while
maximizing captures it tries to minimize the taxi/Manhattan distance to
the opponent’s black king rank. This weaker ai element combination will
be named “TaxiMax” for the occasion and can be viewed as a half-ply plus.

Note this hal-ply thus cannot prevent the king to move in check position.

|=–[ 5.2 – The data constants used

The fixed dataset or constants of BootChess are listed hereunder and
explained thorougly. Even if passive data, they are an essential piece of
the code because only their proper required presentation to processing
can allow significant size reduction.
The piece type move pointer array contains the differential indexed jumps
to the actual list of moves per piece nature. Notice a trick initially
present in 1K ZX Chess : the move offset for the king pointer is the same
as the move offset for the queen pointer because only repetition actually
differenciates the two :

tab db p-tab,r-tab,n-tab,b-tab,q-tab,q-tab

Next is the last chessboard ranks common string FIDE representation in
low-caps, followed by the individual equivalent piece value chosen
bitmasks : important pieces have high values and vice-versa. Lastly is
some fixed-size rle to fill in the pawn and middle ranks sequentially,
for the physical board (presentation) and identical-sized logical board
(values) :

br0 db “rnbqkbnr”
db 8,16,32,64,128,32,16,8
db ‘p’,4,’.’,0,’.’,0,’.’,0,’.’,0,’P’,4

There is indeed a pattern repeating 4 times in the last line (word 1eh)
but remember we are trying to maximize enthropy through the final
ratio : data/processing and a mov count/rep movsb “in situ” ontop
of the clever algorithms used is bound to > 6 bytes which would be bigger
than the 4×2 exploded pattern here – not mentionning having to store
at least once the original pattern in both cases.

We can already envision from the two variables above how a piece’s byte
bit position is really an index into the piece type move pointer array.
The same piece byte doubles as an easy bitmask lending itself to single
byte conditional statements once in the flags register of the x86/x64
architecture (?? means reserved below) :

m is movedb4 and cf bit#0 of flags as carry state (implicit)
x is queened and ?? bit#1 of flags forced bitmask (implicit)
p is pawn and pf bit#2 of flags as parity state (explicit)
r is rook and ?? bit#3 of flags forced bitmask (explicit)
n is knight and af bit#4 of flags as adjust state (explicit)
b is bishop and ?? bit#5 of flags forced bitmask (explicit)
q is queen and zf bit#6 of flags as zero state (explicit)
k is king and sf bit#7 of flags as sign state (explicit)

The least significant bit is explicitely unused : BootChess uses it to
remember if a piece has already been moved once (for example the pawns
can only move 2 squares on their very first move and only horizontally).

An example of the advantage of this choice of bit encoding follows :

and ah,11111101b ; put aside promoted piece
sahf ; 1 byte opcode (hi-byte accumulator in lo-byte flags)
jc movedB4
jp isPawn
ja isKnight
jq isQueen
jk isKing
jmp isRookOrBishop

Knowing the rook and bishops are really not the most problematic cases
hence not the most often condition-tested pieces in programming a chess
game.

The piece nature allowed legal moves are stored just behind and pointed to
by the individual tab pointers described earlier :

bit#2 pf=04 with p[6]=r[0] overlay :
p db 2,3,-10h,-15,-17,10h,15
bit#3 ??=08 with r[5]=n[0] overlay :
r db 17,4,10h,-1h,-10h
bit#4 af=16 with n[9]=b[0] overlay :
n db 1,8,1fh,21h,12h,-0eh,-1fh,-21h,-12h
bit#5 ??=32 with b[5]=q[0] overlay
b db 0eh,4,-0fh,11h,-11h
bit#6 zf=64 k=q except k[0]=1 :
q db 0fh,8,10h,11h,0fh,1h,-10h,-11h,-0fh,-1h

This calls for several remarks. Firstly, the leading words represent
respectively the number of repeats for every piece move listed followed
by total number of total moves, followed by the moves themselves. Each
move is encoded as a single byte linear index displacement (versus two
byte vectorized coordinates) and can only be applied to the first physical
board ; that is not a problem for we are using a so-called “0x88 board”,
one of the 3 main techniques to encode a chess board state along with the
well-known “10×10 boards” and “bitboards” :

The “0x88 board”

physical board logical board
– – – – – – – – – – – – – – – –
| 8 bytes-> | 8 bytes-> |

With the 0x88 representation, several tricks can be used and exhaustively
evoking eaxh is beyond the scope of this article. The most interesting one
is simply it bears the ability to test in a single call if a piece tried
to move out of the bounds of the limits of the board :

test al,88h ; if lo-accumulator=moveOld+moveNew*repeats
jnz outOfBoard ; pretty elegant

But why are the number of moves and repeat per moves numbers all wrong ?
It is because of the overlays used to gain extra bytes : the end byte of
one variable (last move value) doubles as the beginning byte of the next
variable (number of repeats). As long as that overlay value is superior
or equal to the legitimate number of repeats it will be stopped in the
adding loop by the outOfBoard test : hence the re-ordering of the moves
themselves per variable when the last value did not conform (the order
of the tested possible moves have no incidence on the outcome evaluation
results as there is no alpha-pruning) :

p[6]=r[0] overlay : 17>7 => r[0]=7 0x88 test overriden
r[5]=n[0] overlay : 1=1 => n[0]=1 implicit test uneeded
n[9]=b[0] overlay : Oeh>7 => b[0]=7 0x88 test overriden
b[5]=q[0] overlay : 0fh>7 => q[0]=7 0x88 test overriden
k=q except if SF=1 then => k[0]=1 explicitely overidden

Surprisingly not encoding the symetric values in piece moves relying on
run-time code to apply symetry do not yield any satisfiable size code
reduction whilst maintaining processing simplicity of pawns’ special case.

Next follows the “files” index coordinate static string (the right hand
side “ranks” vertical coordinate string is created at run-time) :

bf1 db “abcdefgh”

the Ruy Lopez/Spanish opening is hardcoded as if was initally typed by
the user (or the computer) for whites’ first turn to play. It is simply
recognized by most as the statistically most effective opening to date :

num db “e2e4”

Since the legacy bios/efi x86/x64 can also be tested as a Dos 16-bit
.com file up to the 32-bit version of Win7 with a compatible shortcut :

if x86 ; if x86 boot context environment
sig db 55h,0aah ; boot signature magic string
end if ; end of conditional preprocessing

|=–[ 5.3 – The program flow explained

The main code flow obliges us to think in terms of “thunking”. It must be
simple in its devising and have main robust intersections. The main thunk
is hence the user keyboard input interface. Whatever the player turn is,
whatever routine is currently called it must always solely rely on generic
apprehending of the last bottom line formatted as so :

frFR : old file/old rank/NEW FILE/NEW RANK (in low-caps)

The string is used as a placeholder when the computer tests all
the different combinations possible, then a coordinates to linear index
routine is called for displacements and global verification is applied.

1.if blacks then test keyboard input conforming
if whites then skip test

2.if blacks then skip possible move projection
if whites then do move possible projection

3.if whites’ turn then evaluate (see below)

4.if blacks then verify move conforming
if whites then verify move conforming

5.if esc key or king is destination of legal move then exit
else player=player xor turn and goto 1

Below is the pseudo-code explaining BootChess’ main loop design :

for allPossiblemovesRanks
for allPossiblemovesFiles
possSrcIdx=getLinearIndex(possSrcMovesRank;PossSrcMovesFile)
possDstIdx=getLinearIndex(possDstMovesRank;PossDstMovesFile)
verify |possDstIdx-possSrcIdx| belongs to piece nature move array
if (possSrcIdx;possDstIdx) is valid legal move then
srcIdx=getLinearIndex (of first 2 bytes of keyboard input string)
dstIdx=getLinearIndex (of last 2 bytes of keyboard input string)
if srcIdx=possSrcIdx && dstIdx=possDstIdx then goto found
found:goto evaluate (see 3. below)
else skip found
next allPossiblemovesFiles
next allPossiblemovesRanks
goto notfound

Once a piece is found :

3.if whites’ turn then evaluate as followed :

if pieceValue>best then best=pieceValue
dword of internal input str=dword of keyboard input str
else if found=true and dword of keyboard input str=”????” then
if rank(dst of keyboard input)>rank(dst of internal input)
dword of internal input str=dword of keyboard input str

Lastly after this exhaustive state search :
overwrite keyb input str dword with internal input str dword
goto switch move
if king is dst index then checkmate

|=–[ 5.4 – Analysing random snippet

The ai strategy as the pseudo-code program-flow have been covered. Lastly
and before giving BootChess’ full commented listing hereunder, we analyse
a mildly complex random snippet for the sole purpose of presenting some
specific programming practices exclusively governed by the need to reduce
its final binary footprint.

We will examin the program’s foremost innerloop, executed 4096 times per
player turn (rankSrc*fileSrc*rankDst*fileDst) to test whether the computer
move is not only legal chess-wise but also and if it’s the best possible :

for(rankSrc=0 to 7)
for(fileSrc=0 to 7) innerloop1
for(rankDst=0 to 7) innerloop2
for(fileDst=0 to 7) innerloop3
snippet /*a:”
“A:\>”
4)in DOSBox 0.74 console type “BootChess.com”
BootChess launchs…

Q : Can one compress BootChess with a XT compatible packer such as aPACK?
A : No, despite what Wikipedia claims, packers can never beat the humain
brain, even the best 16-bit packer for years :
C:\chess>apack -v -x BootChess.com
– Code size : 508 bytes
? Packing code -> 521 bytes
? Code mover added -> 20 bytes
? Depacker added -> 99 bytes
Done … compression ratio: -25% (508 bytes -> 640 bytes)

|=–[ 6.2 – BootChess.asm

;———-RED-SECTOR-INC.-proudly-presents-a-33-year-old-record-:———-
; ___ _
; / / _____ _ _ _____ _ _ ___ _
; .::. / / / / / / / / / /
; :::: / / ____ .-/ _ ___/-. .-/ _ ___/-. / /__
; :: / \ | | . | | | . | / /
; :: __ _ \ l | | | l | | / ___/
; .::. / / / / | l |_| l | |__/ / ____
; .::::. / __/ `–‘ `–‘ / |
; :::::::: / / |
; ___ __ Cl! ___ ___ / ___ _ _ __|
; ___ _ _ / __/_ __ _ _/ _/_ _ /_ / / ___ /__
; /_/ / / / / / / _____/ / / / __/ _ / / /
; .-/ ___/ / /______ / ___\ \___ / / __/
; | / / / | __/ ___ | \ | ___\ \___
; | / ____ | / | | _/ | | \ |
; | /–/ | ___/ | | | | | _/ |
; | | / / :: | ____/ :: | | :: \_____| | |
; |_____/ :: | __/ /_______| /_______| |_______\ | :: \_____|
; /_______| /___ _ /___ _ _ ___\ |_______\
; /___ _ _ ___\
; BootChess is the smallest computer implementation of chess on any platform
; Chess in a 512-byte x86 boot sector for Windows / Linux / OS X / DOS / BSD
; Coded by Olivier “Baudsurfer/RSi” Poudade with extra help of Peter “QKumba”
; Ferrie. Logo by Frederic “Cleaner/Break” Cambus. (c)2015 WTFPL v2 license.
; “Fasm BootChess.asm” + “partcopy BootChess.bin 0 200 -f0” = PC floppy boot
;-BootChess.asm——————-;—————————————–
x86 equ 1 ; x86=1 PC/emu vs. win32b/(DOS)Box
saf equ 0 ; saf=0 +queening -exotic failsafe
_b equ byte ; DEFAULT SETTINGS ABOVE ARE FOR A
_w equ word ; STANDARD PC BOOTSECTOR +QUEENING
_d equ dword ; x86=1 saf=0 512b inc. queening
_s equ short ; x86=1 saf=1 500b+ excl. queening
_n equ near ; x86=0 saf=1 506b inc. queening
_f equ far ; x86=0 saf=0 487b excl. queening
if x86 ; beg of boot vs .com preprocessing
org 7c00h ; std start of bootsector after post
if saf ; beg clear any start ambiguous segment
jmp _f 0:fix ; 7c0:0000 vs. 0:7c000 cs para fix-up
end if ; end clear any start ambiguous segment
fix:push cs ; if post int 19h isr bootsrap loader
pop ds ; left any bda or shadow segment values
push cs ; then enforce ds=cs=0
pop es ; then enforce es=ds=cs=0
mov aX,13h ; function set vga mode 320x200x256
else ; else if 16-bit binary assume ah=0
org 100h ; start of com binary program ip
mov aL,13h ; function set vga mode 320x200x256
end if ; end of boot vs .com preprocessing
int 10h ; standard bios video api
brd equ bf1+16 ; chess board at end of sector
mov di,brd ; set physical board index
mov bp,12 ; set 6×8+8 empty sqr mid board lines
call in2 ; pass#1 black “rnbqkbnr” low-caps
push word opn ; pass#2 hi-caps whites & fall-through
rle:lodsb ; al=’.’/al=null (fixed length rle)
mov cl,8 ; empty sqr mid board line length
rep stosb ; set one empty sqr mid board line
dec bp ; all empty sqr mid brd lines inited ?
jnz rle ; if not repeat init else bp=0 assumed
mov ah,’A’-‘a’ ; fall-through pass#2 white hi-caps
in2:mov si,br0 ; si points to endrank “rnbqkbnr” str
if x86=0 ; if .com binary environment ch=0
mov cL,8 ; “rnbqkbnr” endrank str length
else ; assume nothing although tempting
mov cX,8 ; “rnbqkbnr” endrank str length
end if ; end of register ch startup value
in3:lodsb ; read physical board str car
add al,ah ; hi-caps rank 1 / low-caps rank 8
stosb ; write physical board str car
loop in3 ; all “rnbqkbnr” str car written ?
mov cl,8 ; si-;equiv piece vals di-;0x88 brd
rep movsb ; write logical 0x88 board str vals
retn ; return to callers
ge0:mov bx,di ; physical board idx (bx=brd)
mov dh,’1′ ; beg white move src rank
ge1:mov dl,’h’ ; beg white move src file
ge2:mov [si],dx ; beg white move src str
mov ch,’1′ ; beg white move dst rank
ge3:mov cl,’h’ ; beg white move dst file
ge4:mov [si+2],cx ; beg white move dst str
pusha ; save all values
call idx ; passive chess coords to linear indexes
jbe mis ; white move src color not conforming
push bx ; save white move dst idx
call ver ; white move legal chess ?
pop bx ; restore white move dst idx
jc mis ; white move not legal chess
mov di,num+3 ; compare move destination rank in 7dfeh
inc si ; with move source rank in 7dfch
cmpsb ; is taxi distance to topmost bettered ?
jnc wor ; else not getting closer to black king
cmp _b [di],’?’ ; does any fallback move exist yet ?
jz lkj ; no, then last valid move good enough
wor:mov aL,_b[si+bx+brd-num-‘a’+6]; yes, previous valid legal exist so
dec aL ; only override if it’s a capture
js mis ; no, don’t want worse taxi distance
mov bx,fs ; it’s a capture with piece value=al
cmp bL,aL ; but hightest capture value yet ?
jnc mis ; no, less important opponent piece
max:mov fs,bx ; fs=best move yet in taxi half-ply
lkj:dec si ; realign source index
dec si ; to copy dword bst=dword idx
movsd ; after 4096 tries : move=dword bst
mis:popa ; restore all values
cmp cl,’a’ ; end white move dst file ?
loopnz ge4 ; dec white move else next dst file
inc ch ; inc white move dst rank
cmp ch,’9′ ; end white move dst rank ?
jnz ge3 ; else next move dst rank
cpx:inc bx ; inc physical board index
dec dx ; dec white move src file
cmp dl,’`’ ; end white move src file ?
jnz ge2 ; else next move src file
inc dh ; inc white move src rank
cmp dh,ch ; end white move src rank ? ch=9
jnz ge1 ; else next move src rank
push _d [si+4] ; get best white move found
pop _d [si] ; set it as final white move
val:mov cl,’.’ ; valid : empty sqr replaces src piece
call act ; active chess coords to linear indexes
xor bp,3 ; player turn and pawn unidir. delta
jz ge0 ; white turn to play (case best=0)
bla:mov al,’?’ ; input str clear pattern
mov di,si ; input str clear pattern (di=num)
mov cx,8 ; input str clear pattern
rep stosb ; input str clear pattern (di=brd)
call key ; get user keyboard input
jbe bla ; black move src color not conforming
opn:call ver ; di=brd, black move legal chess ?
jc bla ; white move not legal chess
jmp _s val ; validate black move
ver:call idx ; get lin indexes /w implicit passive
xchg bx,dx ; switch bx=dst idx dx=src idx
mov ah,[si+bx+brd-num-‘a’+8] ; get piece logical 0x88 brd val…
mov dh,bl ; dh=src idx dl=dst idx
sub dx,”aa” ; get move file zero-based indexes
bsr bx,ax ; scan for 1st bit set (si=idx+10)
movsx bx,[si+bx-10-num+tab] ; bl=moved piece type idx (bh=0)
mov cx,_w [si+bx-num+tab] ; piece type deltas cl=repeats ch=num
sahf ; set piece logical 0x88 brd val
jnp sp1 ; branch if piece not pawn (bit#4!=1)
jc sp2 ; branch if pawn prev moved (bit#0=1)
sp1:jns sp3 ; branch if piece not king (bit#7!=1)
sp2:mov cl,1 ; override repeat if piece pawn or king
sp3:jnp sp4 ; branch if piece not pawn (bit#4!=1)
add bx,bp ; pawn player turn unidirection mutex
sp4:inc bx ; advance piece type struct field ptr
and ah,11111100b ; isolate piece bitmask only
vl1:push cx ; save piece type deltas
mov al,dh ; load start dst idx val
inc bx ; advance piece type struct field ptr
vl2:add al,[si+bx-num+tab] ; add this piece delta to dst idx val
xchg aL,bL ; base reg=dst idx val and preserved
mov ch,[si+bx+brd-num+8] ; read projected dst square val
xchg aL,bL ; base reg=piece type struct field ptr
cmp al,dl ; wanted move found (src+delta(s)=dst) ?
jnz dif ; different than requested move
sam:sahf ; get piece logical 0x88 brd val in flgs
jnp yes ; branch if piece is not pawn (bit#2=0)
test [si+bx-num+tab],1 ; pawn piece delta parity=diag vs. vert
jz ord ; branch if pawn piece moving vert
test ch,ch ; pawn piece vert move=;eating ?
jnz yes ; capturing verify dst sqr not empty
jmp _s bad ; else illegal chess move is a miss
ord:test ch,ch ; pawn piece vert move=;no eating ?
jz yes ; no eating=;empty dst sqr else illegal
dif:sahf ; store piece nature in flags register
jnp skp ; not pawn piece so skip direction test
test [si+bx-num+tab],1 ; pawn piece delta parity=diag vs. vert
jnz bad ; diagonal pawn move is illegal
skp:test ch,ch ; else skipping over dst square val ?
jnz bad ; projected dst sqr val is not empty
sahf ; get piece logical 0x88 brd val in flgs
jz x88 ; branch if piece is queen (bit#6=1)
jna bad ; branch if piece is not knight(bit#4=0)
x88:test al,88h ; ch=0 dst out of physical board limits?
loopz vl2 ; else cont if delta repeats remain
bad:pop cx ; restore piece type deltas
dec ch ; all possible delta nums verified ?
jnz vl1 ; if not then cont next delta type
nok:stc ; else return /w no match flg set
retn ; return to caller
yes:pop cx ; correct entry sp and disregard count
retn ; return to caller(s)
key:call prt ; refresh screen to account input echo
xor bx,bx ; bx=str idx=odd/even/alpha/num mutex
kbd:cbw ; fun blocking wait for keystroke (ah=0)
int 16h ; std bios keybd api (ah=scan al=ascii)
esc:dec ah ; was esc key pressed to quit ?
jnz car ; else default process key input
xit:if x86 ; if x86 boot context environment
int 19h ; exit through bootstrap to reboot cpu
else ; else if .com 16-bit binary
int 20h ; dos 1+ – terminate program
end if ; end of exit methods (os load or shell)
car:mov [bx+si],al ; sav ascii val to move string (si=num)
prt:pusha ; save game state snapshot
cwd ; curs location dx=(0,0)=(row,column)
mov ax,1301h ; function ega write str write mode 1
mov bl,7 ; page 0 grey car attrib matching tty
mov cl,8 ; src str lngth (curs updated horiz)
mov bp,bf1 ; es:bp is “abcdefgh” ptr
lns:int 10h ; standard bios video api
add bp,16 ; bp=para step siz separating strings
push ax ; save old bios video api func params
mov ax,0e39h ; function teletype outp car=rank ‘9’
sub al,dh ; decrement right handside rank value
int 10h ; standard bios video api
pop ax ; restore old bios video api fx params
cmp dh,cl ; src str total (curs updated vert)
inc dh ; preemptive off-by-one allows 9 verts
jc lns ; all 9 brd gui row strings printed ?
mov bp,si ; 10th row tail bp=move coords, cl=8
int 10h ; standard bios video api
popa ; restore game state snapshot
inc bx ; test if any more keys ?
cmp bl,4 ; frFR format input string
jc kbd ; else continue input
idx:loop idx ; ch=0 passive call load src/dst lin idx
act:mov si,num ; reinit si to point to coord input str.
mov bx,[si] ; bx=src coord (pass#1)
cbw ; empty sqr val in logical 0x88 board
call put ; place param passed as fun pass#1
mov dx,[si+2] ; bx=dst idx dx=src idx
xchg bx,dx ; fall-through for second pass
push word mat ; test for checkmate and conforming
put:xchg ax,bx ; bx|dx=[num+di]+16*((8-‘0′)-[num+di+1])
aad -10h ; shl ah,4/sub al,ah/xor ah,ah
add al,80h ; bx|dx=al-640%256-16*ah
xchg ax,bx ; bx|dx=al+128-16*ah
jcxz sim ; active call request or simulation ?
if saf=0 ; standard non-failsafe queening
cmp _b [si+3],’8′ ; validated dst rank is top-most ?
jz qq ; if so then promote pawn to queen
cmp _b [si+3],’1’ ; validated dst rank is bottom-most ?
jnz prm ; if not no pawn queening promotion
qq: sahf ; store piece nature in flag register
jnp prm ; no pawn queening promotion
xor ah,01000110b ; transform p to promoted queen
inc cx ; queen promotion p2q or P2Q
end if ; end of conditional queening
prm:xchg ah,[si+bx+brd-num-‘a’+8] ; update piece logical 0x88 board val
xchg cl,[si+bx+brd-num-‘a’] ; update piece physical board ascii val
or ah,1 ; update piece moved once (bit#0)
sim:retn ; return to caller(s)
mat:sahf ; catured piece king and mate ?
js xit ; if piece is king then game is over
call chk ; move src color conforming ?
jnz nok ; move src color not conforming
chk:xchg bx,dx ; src idx mov al,[si+bx+brd-num-‘a’] ; pass#1:src idx pass#2:dst idx di=brd
xor _b [si+len-num],8 ; self-modif 8/26 val=[1;8]/[a(A);z(Z)]
mov cl,-‘a’ ; assert black piece car interval
test bp,bp ; test whose turn it is to play
jnz lim ; assert white piece car interval
mov cl,-‘A’ ; al=ascii value cl=-(lower boundery)
lim:xadd al,cl ; tmp=al+cl cl=al al=tmp +fall-trough
db 0d4h ; aam
len:db 12h ; ah=al/8 al%=8
mov al,cl ; al=restored ascii value
test ah,ah ; set/clear zf=0 success zf=1 failure
retn ; return to caller(s) nb: destroys ah
tab db p-tab,r-tab,n-tab,b-tab ; piece type mov offset array
db q-tab,q-tab ; note original 1K ZX Chess q=k trick
br0 db “rnbqkbnr”,8,16,32,64,128 ; end rank pattern + beg piece values
db 32,16,8,’p’,4,’.’,0,’.’,0 ; end piece values + beg mid board reps
db ‘.’,0,’.’,0,’P’,4 ; … end mid board reps
p db 2,3,-10h,-15,-17,10h,15 ; bit#2 pf=04 p[6]=r[0] overlay
r db 17,4,10h,-1h,-10h ; bit#3 ??=08 r[5]=n[0] overlay
n db 1,8,1fh,21h,12h,-0eh,-1fh ; bit#4 af=16 n[9]=b[0] overlay
db -21h,-12h ; … end of knight moves list
b db 0eh,4,-0fh,11h,-11h ; bit#5 ??=32 b[5]=q[0] overlay
q db 0fh,8,10h,11h,0fh,1h,-10h ; bit#6 zf=64 k=q except k[0]=1
db -11h,-0fh,-1h ; … end of queen/king moves list
bf1 db “abcdefgh” ; gui file index string
num db “e2e4″ ; hardcoded Ruy Lopez opening
if saf and x86 ; x86 failsafe exotic boot environment
times 510-($-$$) db 0 ; nul padding if necessary
org 7df0h ; boot signature vbr/mbr standard offset
sig db 55h,0aah ; magic number no-endian boot signature
end if ; end of conditional failsafe signature

|=–[ 6.3 – Encoded base64 floppy image

This is BootChess_500b_noQueening.img.7z.uue for Bochs v2.6 :

begin 644 FLOPPY.7Z
M-WJ\KR<<“/$P0TX”0,“““`!=““““`):A<QL`=0%+!`O%F,\C).-Z M;$=*5]JZ\`-6,?XTW!KSPJ’1-6G$ARFF9!HS3,#)’)1N7*D;8SS;”#0\60AE M-[CNTB+D/0Z9#=:_9P2KN#\MYXCNQEJ4BH1SX>/1=Y.`CHNM%WS\8U`$`F]A
ME2F#R_9K`29FA1V18\#-B:U’D*!T7:#C@1E\F9V(:ADLP`IM:_8Y*V’DZ8&R
M^3E%4FY*)6’@1\[,#&`C8,]([!@(HMOQ”W\G<F[88BP>-@<]WC*%UH*N^X%-
M[WY$$I9OUY2)N@1,XML;SK@NNA3&38?K<%CX1W4″L5!5W2S395P18V+\0=V”
MJWNAR%@(*3_.:<P-:G1ZO[%@A5**6J&Y3)!<5HFF0B,HLD,J\S:U./[EM:-X
MYP8KC)<=B(;IT”:8((FP%_*K904!T?\7\4K,+.C04OHG9SC[O[“\!W”M%_L^ M?%$0.4L.X3.G&\+FC?Y;9(`J+U”.:ZCS>H,6O^&21__MUPNQBLD&>EBP2%S< M%:(#KYU5?5E?R;S>).`S)AMFCK^>;$X&3\C4H)>2V_I+I3G29!?CM^@P’A1\
M;>P”I59*Q3Y71<^834CZT/<14KJ.U*`42>PDP%N^.”:ED\/)96K[*YB5#=NW
M;.JWHJ’C-GI!UCI1&7″#DB;Z4VE’/$W7/X^=$<H_+>Z1MR9-NU”:DB8Q:5=>
M1\VA)\TS&7OZZ:IKAN+KC:P=4?76V>:H:24^AT4S;JOY)@JWNMC>`AUI-K)+
M,Y2F890+C’`)T1$B258[[S\F6D>&=F>2@”56SM+N&<\B+\Z_G-H`/JTFP$“ MC[496E4YX;VB/E9E?DCTT’16@YP$;LR]V>B*K9<M_R[KFAVZSK$G&%Q9ANEF M4EB^Z7:L6>3E6P4(^ M+E^J#SY+9D*0$P[_$)/X<7A9^`O-_Y4H1@^I_’S>^YHP+E;`CX7S@X’`9<0E
M4_CUD38Q!:6P[F_!<$U’#-&1$:JM8!VZSK$G&%Q9ANEF4EB^Z7:L*”W<Q“!
M!`8“0F#”0`'”P$“2,#`0$%70“&“,U@”“`@*`3?;3$L“`4!$1T`0@!O
M`&\`=`!#`&@`90!S`’,`+@!I`&T`9P“`!0*`0!.7V=GM#G0`14&`0`@(“`
“““
`
end

|=–[ 7 – Thanks/Greets ]=———————————————-=|

Thanks to Peter “QKumba” Ferrie for accepting to peer-review my code.
Thanks to The Phrack Staff for the final proofreading of the article.
Thanks to Frederic “Cleaner/Bloctronics^Break” Cambus for ascii logo.

Greets to Abrasax/Quartex, Alk/Titan, Alpha Flight, Astrofra/Mandarine,
Carbon/Atari Active, Calsoft/Psycho Hacking Force, CiH/Alive Team, Codex,
Cronos/Amazing Cracking Conspiracy, Cyg/BlaBla, D-Bug,Dbug/Defence Force,
Dma-Sc/M4nkind, Defjam/Checkpoint, Den/Top Right Bottom Left,Felice/Alive
Team, Feust/Red Sector Inc., Floyd/Surprise productions, Flure/Popsy Team
FroST/Red Sector Inc., Gengis/Bomb, GGN/KUA Soft, Grazey/Psycho Hacking
Force, Havoc/LineOut, HellMood/Desire, Hitch Hikr/Skidrow, Hotshot/Dark
Bit Factory, Imerso/Red Sector Inc., Irokos/Titan, KiNG1/Red Sector Inc.,
Kirl/Dark Bit Factory, Leon Bli/Red Sector Inc., Lexomil/Red Sector Inc.,
Lotek Style/tSCc, Made/Bomb, MMyErs/Red Sector Inc., Okkie/Accession, P01
/Ribbon, P0ky/Flush, Pasy/Rebels, Photon/Scoopex, Principal/American
Cracking Association, Ramon B5/Desire, Raizor/fnuque, Rez/Razor 1911,
RiOT, Sam/Debian, ShockWave/Dark Bit factory, Sim/Resistance, Skamp
/Vacuum, Skeleton/The Pirate Ship, Skywalker/Paradox, SnC/Red Sector Inc.
Stingray/Scoopex, Thorin/Melon Dezign., Tinker/LineOut, TomoAlien/Bon
Squared, t0my/Live!, Troy/Sensensthal, Ttemper/Red Sector Inc., UkkO
/Live!, Val/@party, Wullon/adinpsz, Wietze/#atariscne, Xeu/EXA, XiA
/Checkpoint, XXX/Haujobb, Zerkman/Sector One

|=–[ 8 – References ]=————————————————-=|

[0] https://github.com/bloovis/isis/blob/master/intel/utils/conv86
[1] http://goo.gl/UnXUMX “1K javascript madness”
[2] http://www.benlo.com/microchess/index.html
[3] http://goo.gl/TLJJHq http://home.kpn.nl/a.dikker1/museum/databus.html
[4] http://www.pouet.net/prodlist.php?type[0]=64b&order=thumbup
[5] http://en.wikipedia.org/wiki/1K_ZX_Chess
[6] https://code.google.com/p/corkami/wiki/InitialValues
[7] http://www.pouet.net/which=62445 http://youtu.be/ygtuB-L88rQ
[8] http://bochs.sourceforge.net/cgi-bin/lxr/source/bios/rombios.c
[9] http://wiki.osdev.org/MBR_x86) “Initial Environment”
[a] http://en.wikipedia.org/wiki/Volume_boot_record#SIG
[b] http://www.fide.com/fide/handbook.html?id=171&view=article “Chess Laws”
[c] http://www.kuro5hin.org/story/2001/8/10/12620/2164
[d] http://goo.gl/Tn203B “Full ZX-81 CHESS in 1K”
[e] http://www.phrack.org/papers/fall_of_groups.html
[f] http://goo.gl/7md7YJ “Where can I find 8080 to x86 assembler conv…”

|=[ EOF ]=—————————————————————=|[/codee]

下载地址

源代码

赞(0) 打赏
未经允许不得转载:枣庄滕州微信小程序开发_wordpress主机SEO优化_滕州网站建设 -眼镜男网络 » 世界上最小的国际象棋程序
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!