Play this at http://johnearnest.github.io/Octo/?gist=c660fd132433a947a88e !
-
-
Save rmmh/c660fd132433a947a88e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"tickrate":15,"fillColor":"#222","backgroundColor":"#CC8","buzzColor":"#FFAA00","quietColor":"#000000","shiftQuirks":false,"loadStoreQuirks":false} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Chip8 Chess | |
# Ryan Hitchman, Dec 2014 | |
:alias selectedpos vA | |
:alias cursorpos vB | |
:alias piecepos vC | |
:alias piece vD | |
:alias undopos v9 | |
:alias rayoff v6 | |
:alias rayind v7 | |
:alias scanpos v8 | |
: main | |
hires | |
cursorpos := 0x46 | |
selectedpos := -1 | |
clear | |
draw-board | |
draw-pieces | |
loop | |
draw-cursor | |
handle-keys | |
again | |
: draw-pos # (pos:v0 image:i -- x:v0 y:v1) | |
v1 := 0b01110000 | |
v1 &= v0 | |
v1 >>= v1 | |
v0 <<= v0 | |
v0 <<= v0 | |
v0 <<= v0 | |
v0 <<= v0 v0 >>= v0 # strip MSB | |
v0 += 32 | |
sprite v0 v1 8 | |
; | |
: draw-cursor | |
i := cursor | |
v0 := cursorpos | |
draw-pos | |
v0 += -1 | |
i := line | |
sprite v0 v1 8 | |
; | |
: draw-board | |
# draw guidelines on left/right | |
v0 := 0 # y | |
v1 := 30 # left | |
v2 := 96 # right | |
i := line | |
loop | |
sprite v1 v0 8 | |
sprite v2 v0 8 | |
v0 += 8 | |
if v0 != 64 then again | |
# now draw grid pixels | |
v0 := 7 # y | |
v1 := 31 # x | |
loop | |
loop | |
sprite v1 v0 1 | |
v1 += 8 | |
if v1 != 103 then | |
again | |
v1 := 31 | |
v0 += 8 | |
if v0 != 71 then | |
again | |
; | |
: draw-piece # (piecepos -- v0:x v1:y) | |
i := board | |
i += piecepos | |
load v0 | |
if v0 != 0 begin | |
i := board-sprites | |
# i := board-sprites[piece/2] | |
v0 >>= v0 | |
i += v0 | |
v0 := piecepos | |
draw-pos | |
end | |
; | |
: draw-pieces | |
piecepos := 0 | |
loop | |
draw-piece | |
# deep magic | |
# pos is 0b0YYY0XXX, so if XXX overflows | |
# it will cascade into incrementing Y | |
piecepos += 0b00001001 | |
v0 := 0b11110111 | |
piecepos &= v0 | |
# if Y overflowed into the MSB, we're done | |
v0 <<= piecepos | |
if vF == 0 then again | |
; | |
: handle-keys | |
v3 := key | |
draw-cursor | |
v0 <<= v3 | |
v1 := 0b01110111 # mask for keys | |
jump0 key-handlers | |
: key-handlers | |
; ; ; ; # x 1 2 3 | |
; jump move-up jump select-piece # q w e | |
jump move-left jump move-down jump move-right # a s d | |
jump undo-move ; ; # z c 4 | |
; ; ; # r f v | |
# use 3-bit two's complement to overflow instead of borrowing | |
: move-left cursorpos += 0b00000111 cursorpos &= v1 ; | |
: move-right cursorpos += 0b00000001 cursorpos &= v1 ; | |
: move-up cursorpos += 0b01110000 cursorpos &= v1 ; | |
: move-down cursorpos += 0b00010000 cursorpos &= v1 ; | |
: select-piece | |
# is this a position we can move to? | |
i := cursor-probe | |
v0 := cursorpos | |
draw-pos | |
v2 := vF | |
v0 := cursorpos | |
draw-pos # erase probe | |
if v2 == 1 begin | |
move-piece | |
selectedpos := -1 | |
else | |
if selectedpos != -1 then gen-moves | |
if cursorpos == selectedpos begin | |
# unselect piece | |
selectedpos := -1 | |
return | |
end | |
piecepos := cursorpos | |
selectedpos := cursorpos | |
gen-moves | |
end | |
; | |
: move-piece # move piece (selectedpos => cursorpos) | |
piecepos := cursorpos | |
v3 := 0 | |
v1 := -1 | |
draw-piece # (maybe) erase captured piece | |
if v1 != -1 begin | |
# capturing a piece; push undo info | |
# LITERAL: AAAAAAA0 BBBBBBBM | |
v3 := 1 # remember that we're capturing | |
v1 <<= piecepos | |
i := board | |
i += piecepos | |
load v0 | |
undo-push | |
end | |
# moving a piece; push undo info | |
# MOVE: AAAAAAA1 BBBBBBBM | |
v1 := 1 | |
v0 <<= cursorpos | |
v0 |= v1 # A...A1 for move | |
v1 <<= selectedpos | |
# if we're capturing, there's a previous command | |
v1 |= v3 | |
undo-push | |
#:breakpoint capture-erased | |
piecepos := selectedpos | |
gen-moves # erase threats | |
#:breakpoint threats-erased | |
draw-piece # erase moving piece | |
i := board | |
i += selectedpos | |
load v0 | |
v1 := v0 | |
i := board | |
i += selectedpos | |
v0 := 0 | |
save v0 | |
#:breakpoint moving-erased | |
v0 := v1 | |
i := board | |
i += cursorpos | |
save v0 | |
piecepos := cursorpos | |
draw-piece # draw new piece | |
; | |
: maybe-test-scanpos | |
# check for out-of-bounds | |
v0 := 0b10001000 | |
v0 &= scanpos | |
if v0 == 0 then jump test-scanpos | |
v0 := 0 | |
v1 := 0 | |
return | |
: test-scanpos # (scanpos -- v0:empty, v1:capture) | |
i := board | |
i += scanpos | |
load v0 | |
if v0 == 0 begin | |
v0 := 1 | |
v1 := 0 | |
else | |
# there's a piece here. is it an opponent's piece that we can capture? | |
v1 := v0 | |
v1 ^= piece | |
v1 <<= v1 | |
v1 := vF | |
v0 := 0 | |
end | |
; | |
: gen-moves | |
i := board | |
i += piecepos | |
load v0 | |
piece := v0 | |
# TODO: side check | |
if piece == 0 then return | |
rayind := 0b01111111 | |
rayind &= piece | |
if rayind == 0x40 begin # is it a pawn? | |
# move up or down based on side | |
rayoff := -16 | |
v0 <<= piece | |
if vF == 1 then rayoff := 16 | |
scanpos := piecepos | |
scanpos += rayoff | |
test-scanpos | |
if v0 == 1 begin | |
emit-move | |
# are we at the starting rank for the pawn? | |
v0 := 0b01110000 | |
v0 &= piecepos | |
v1 := 0x60 | |
if rayoff == 16 then v1 := 0x10 | |
if v0 == v1 begin | |
scanpos += rayoff | |
test-scanpos | |
if v0 == 1 then emit-move | |
end | |
end | |
# TODO: en-passant | |
scanpos := piecepos | |
scanpos += rayoff | |
scanpos += -1 | |
maybe-test-scanpos | |
if v1 == 1 then emit-move | |
scanpos += 2 | |
maybe-test-scanpos | |
if v1 == 1 then emit-move | |
return | |
end | |
# TODO: castling | |
loop # foreach ray directions | |
i := offset-base | |
i += rayind | |
load v0 | |
while v0 != 0 # end of ray table | |
rayoff := v0 | |
rayind += 1 | |
scanpos := piecepos | |
loop # slide along | |
scanpos += rayoff | |
# piece offset is 0rrr0fff, so moving off the board can be detected | |
# by testing for overflow/underflow into side bits | |
v0 := 0b10001000 | |
v0 &= scanpos | |
while v0 == 0 | |
test-scanpos | |
if v1 == 1 then emit-move | |
if v0 == 1 begin | |
emit-move | |
# knights (5) and kings (6) only move once | |
v0 := 0b01000000 | |
v0 &= piece | |
if v0 == 0 then | |
again | |
end | |
# check if we've already done 8 rays | |
v0 := 0b1000 | |
v0 &= rayind | |
if v0 == 0 then | |
again | |
; | |
: emit-move | |
i := cursor-threat | |
v0 := scanpos | |
draw-pos | |
; | |
# The undo stack allows reverting any change in the board state. | |
# It supports two commands: | |
# Place value A*2 at B: AAAAAAA0 BBBBBBBM | |
# Move byte from A to B: AAAAAAA1 BBBBBBBM | |
# Positions are indexes into the board. | |
# M is set if there is another command in this move. | |
# Note that 0x0 0x0 is a harmless no-op! | |
: undo-move | |
loop | |
undo-pop | |
v3 >>= v1 | |
v4 := vF # last command? | |
v2 >>= v0 | |
if vF == 1 begin # move byte | |
i := board | |
i += v2 | |
load v0 | |
piecepos := v2 | |
v2 := v0 | |
draw-piece # trashes v0, v1 | |
v0 := 0 | |
i := board | |
i += piecepos | |
save v0 | |
v0 := v2 | |
end | |
# else case: literal value | |
# v0 already holds the correct value | |
i := board | |
i += v3 | |
save v0 | |
piecepos := v3 | |
draw-piece | |
if v4 == 1 then again | |
; | |
: undo-push # (v0 v1 -- ) | |
i := undo-stack | |
i += undopos | |
save v1 | |
undopos += 2 | |
; | |
: undo-pop # ( -- v0 v1 ) | |
undopos += -2 | |
i := undo-stack | |
i += undopos | |
load v1 | |
; | |
# piece is: SPPP0000, where S is 0/1 for white/black, and P is piece number [1-6] | |
# board is 8x16, with piece index 0rrr0fff | |
# this allows the 0x88 optimization, where invalid positions can be detected | |
# by checking for overflow into the 0 bits | |
# (and we can stuff some useful data on the side) | |
: board | |
0x90 0xD0 0xA0 0xB0 0xE0 0xA0 0xD0 0x90 | |
: offset-base 0 0 0 0 0 0 0 0 | |
0xC0 0xC0 0xC0 0xC0 0xC0 0xC0 0xC0 0xC0 | |
: rook-offset -16 -1 1 16 0 0 0 0 | |
0 0 0 0 0 0 0 0 | |
: bishop-offset -17 -15 15 17 0 0 0 0 | |
0 0 0 0 0 0 0 0 | |
: queen-offset -17 -16 -15 -1 1 15 16 17 | |
0 0 0 0 0 0 0 0 | |
0 0 0 0 0 0 0 0 # pawn-offset, special-cased | |
0 0 0 0 0 0 0 0 | |
: knight-offset -33 -31 -18 -14 14 18 31 33 | |
0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 | |
: king-offset -17 -16 -15 -1 1 15 16 17 | |
0x10 0x50 0x20 0x30 0x60 0x20 0x50 0x10 | |
: board-sprites # this is indexed by piece/2, but 0x00, 0x70, and 0x80 are invalid pieces | |
: line 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 # invalid piece 0x00 | |
: white-rook 0xaa 0xd6 0x82 0x44 0x44 0x82 0xfe 0x00 | |
: white-bishop 0x7c 0x92 0xba 0x92 0x54 0x82 0xfe 0x00 | |
: white-queen 0xaa 0xaa 0xaa 0x7c 0x44 0x82 0xfe 0x00 | |
: white-pawn 0x38 0x44 0x44 0x28 0x44 0x82 0xfe 0x00 | |
: white-knight 0x14 0x68 0x94 0xc4 0x22 0x42 0xfe 0x00 | |
: white-king 0x10 0x38 0x92 0xee 0x44 0x82 0xfe 0x00 | |
: cursor 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff # invalid piece 0x70 | |
: cursor-threat 0x00 0x00 0x3A 0x28 0xB8 0x00 0x00 0x00 # invalid piece 0x80 | |
: black-rook 0xaa 0xfe 0xfe 0x7c 0x7c 0xfe 0xfe 0x00 | |
: black-bishop 0x7c 0xee 0xc6 0xee 0x6c 0xfe 0xfe 0x00 | |
: black-queen 0xaa 0xaa 0xaa 0x7c 0x7c 0xfe 0xfe 0x00 | |
: black-pawn 0x38 0x7c 0x7c 0x38 0x7c 0xfe 0xfe 0x00 | |
: black-knight 0x14 0xe8 0xf8 0x38 0x7c 0xfe 0xfe 0x00 | |
: black-king 0x10 0x38 0x92 0xfe 0x7c 0xfe 0xfe 0x00 | |
# this bit is set by cursor-threat, but not by any of the pieces | |
: cursor-probe 0x00 0x00 0x00 0x00 0x80 0x00 0x00 0x00 | |
: undo-stack # use the rest of memory |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment