I honestly thought programming a knight's moves would take thousands of lines just to make the piece "think". Turns out it only takes a handful
... Anyway, since it's so lightweight now, what if we tried a fun 4x4 version just packed with twin knights ? .... lol
Code:
' ==================
' KNIGHTSBRIDGE 4x4
' ==================
#win32
set window "Knightsbridge 4x4", 260, 260, false, 2
set redraw off
randomize clock()
' Constants
constant BOARD_SIZE = 4
constant SQUARE_SIZE = 60
constant BOARD_X = 10
constant BOARD_Y = 10
constant HUMAN = 1
constant AI_PLAYER = 2
' Load Assets
visible black = loadimage("assets/black2.png")
visible white = loadimage("assets/white2.png")
' Globals
visible board = dim(BOARD_SIZE, BOARD_SIZE) ' 0=empty, 1=human, 2=AI
visible captures = dim(3) ' captures[1]=human, captures[2]=AI
visible knight_dx = dim(8)
visible knight_dy = dim(8)
visible selected_r , selected_c
visible current_turn=HUMAN, game_over=false, winner
visible mx, my, click_handled
visible last_ai_fr = -1, last_ai_fc = -1
visible last_ai_tr = -1, last_ai_tc = -1
init_knight_moves()
init_board()
'-----------
' MAIN LOOP
'-----------
do
draw_board()
mx = mousex()
my = mousey()
if not game_over then
if mousebutton(0) = 1 and click_handled = 0 then
click_handled = 1
if mx >= BOARD_X and mx < BOARD_X + BOARD_SIZE * SQUARE_SIZE and
my >= BOARD_Y and my < BOARD_Y + BOARD_SIZE * SQUARE_SIZE then
col = int((mx - BOARD_X) / SQUARE_SIZE)
row = int((my - BOARD_Y) / SQUARE_SIZE)
if selected_r = -1 then
if board[row][col] = HUMAN then
selected_r = row
selected_c = col
endif
else
if is_valid_knight_move(selected_r, selected_c, row, col, HUMAN) then
do_move(selected_r, selected_c, row, col)
selected_r = -1
selected_c = -1
current_turn = AI_PLAYER
else
if board[row][col] = HUMAN then
selected_r = row
selected_c = col
else
selected_r = -1
selected_c = -1
endif
endif
endif
else
selected_r = -1
selected_c = -1
endif
endif
if mousebutton(0) = 0 then click_handled = 0
if current_turn = AI_PLAYER and game_over = false then
ai_make_move()
endif
else
if keydown(KEY_Y, false) then
init_board()
selected_r = -1; selected_c = -1
current_turn = HUMAN
game_over = false
winner = 0
last_ai_fr = -1 ;last_ai_tr = -1
last_ai_fc = -1 ;last_ai_tc = -1
click_handled = 0
endif
if keydown(KEY_N, false) then
end
endif
endif
redraw
fwait 60
loop
'------------
' FUNCTIONS
'------------
function init_knight_moves()
'8 possible L-shaped moves a knight can make
knight_dx[0] = 2 ; knight_dy[0] = 1 ' 2 squares right, 1 square down
knight_dx[1] = 2 ; knight_dy[1] = -1
knight_dx[2] = -2 ; knight_dy[2] = 1 ' 2 squares left, 1 square down
knight_dx[3] = -2 ; knight_dy[3] = -1
knight_dx[4] = 1 ; knight_dy[4] = 2
knight_dx[5] = 1 ; knight_dy[5] = -2 ' 1 square right, 2 squares up
knight_dx[6] = -1 ; knight_dy[6] = 2
knight_dx[7] = -1 ; knight_dy[7] = -2
endfunc
function init_board()
for r = 0 to BOARD_SIZE - 1
for c = 0 to BOARD_SIZE - 1
board[r][c] = 0
next
next
' AI knights (top two rows, staggered)
for c = 0 to BOARD_SIZE - 1 step 2
board[0][c] = AI_PLAYER
next
for c = 1 to BOARD_SIZE - 1 step 2
board[1][c] = AI_PLAYER
next
' Human knights (bottom two rows, staggered)
for c = 1 to BOARD_SIZE - 1 step 2
board[2][c] = HUMAN
next
for c = 0 to BOARD_SIZE - 1 step 2
board[3][c] = HUMAN
next
captures[1] = 0
captures[2] = 0
endfunc
function is_valid_knight_move(sr, sc, tr, tc, player)
dr = abs(tr - sr)
dc = abs(tc - sc)
'Verify Knight's L-Shape move
if (dr = 1 and dc = 2) or (dr = 2 and dc = 1) then
if tr >= 0 and tr < BOARD_SIZE and tc >= 0 and tc < BOARD_SIZE then
if board[tr][tc] = 0 or board[tr][tc] <> player then
return true
endif
endif
endif
return false
endfunc
function do_move(sr, sc, tr, tc)
'whose piece is moving, 1=human, 2=computer
mover = board[sr][sc]
'read what's currently on the destination square (tr,tc)
'0=empty, 1=human, 2=computer
captured = board[tr][tc]
'place the piece on destination
board[tr][tc] = mover
board[sr][sc] = 0
'check if a capture occured
if captured <> 0 then
captures[mover] = captures[mover] + 1
if captures[mover] >= 3 then 'victory condition
game_over = true
winner = mover
endif
endif
endfunc
function draw_board()
set color 0, 0, 0; cls
for r = 0 to BOARD_SIZE - 1
for c = 0 to BOARD_SIZE - 1
if (r + c) % 2 = 0 then
set color 200, 220, 200
else
set color 100, 140, 100
endif
draw rect BOARD_X + c * SQUARE_SIZE, BOARD_Y + r * SQUARE_SIZE,
SQUARE_SIZE, SQUARE_SIZE, true
next
next
for r = 0 to BOARD_SIZE - 1
for c = 0 to BOARD_SIZE - 1
if board[r][c] > 0 then
x = BOARD_X + c * SQUARE_SIZE + 5
y = BOARD_Y + r * SQUARE_SIZE
if board[r][c] = HUMAN then
draw image white, x, y
else
draw image black, x, y
endif
endif
next
next
' Highlight AI's last move FROM
if last_ai_fr >= 0 then
x = BOARD_X + last_ai_fc * SQUARE_SIZE
y = BOARD_Y + last_ai_fr * SQUARE_SIZE
set color 255, 0, 0
draw rect x, y, SQUARE_SIZE, SQUARE_SIZE, false
draw rect x+2, y+2, SQUARE_SIZE-4, SQUARE_SIZE-4, false
endif
' Highlight AI's last move TO
if last_ai_tr >= 0 then
x = BOARD_X + last_ai_tc * SQUARE_SIZE
y = BOARD_Y + last_ai_tr * SQUARE_SIZE
set color 255, 0, 0
draw rect x, y, SQUARE_SIZE, SQUARE_SIZE, false
draw rect x+2, y+2, SQUARE_SIZE-4, SQUARE_SIZE-4, false
endif
' Hightlight Human Knight
if selected_r >= 0 then
x = BOARD_X + selected_c * SQUARE_SIZE
y = BOARD_Y + selected_r * SQUARE_SIZE
set color 0, 255, 0
draw rect x, y, SQUARE_SIZE, SQUARE_SIZE, false
draw rect x+2, y+2, SQUARE_SIZE-4, SQUARE_SIZE-4, false
endif
if game_over then
set color 0, 0, 0
draw rect BOARD_X + 20, BOARD_Y + 50, 200, 110, true
set color 255, 255, 255
draw rect BOARD_X + 22, BOARD_Y + 52, 196, 106, false
if winner = HUMAN then
set color 0, 255, 0
set caret BOARD_X + 120, BOARD_Y + 80
center "YOU WIN!"
else
set color 255, 0, 0
set caret BOARD_X + 120, BOARD_Y + 80
center "AI WINS!"
endif
set color 255, 255, 255
set caret BOARD_X + 120, BOARD_Y + 105
center "Restart (Y/N)?"
endif
endfunc
function ai_make_move()
'Step 1 : Initialize fallback variables
' fr/fc = from row/column
' tr/tc = to row/column
fr = -1 ; fc = -1 ; tr = -1 ; tc = -1
'Step 2 : Scan the entire board
for r = 0 to BOARD_SIZE - 1
for c = 0 to BOARD_SIZE - 1
'Step 3 : find AI Knights
if board[r][c] = AI_PLAYER then
'Step 4 : Test all 8 moves
for k = 0 to 7
nr = r + knight_dy[k]
nc = c + knight_dx[k]
'Step 5 : validate the move
if is_valid_knight_move(r, c, nr, nc, AI_PLAYER) then
'Step 6 : prioritize to capture
if board[nr][nc] = HUMAN then
do_move(r, c, nr, nc)
last_ai_fr = r ; last_ai_fc = c
last_ai_tr = nr ; last_ai_tc = nc
current_turn = HUMAN
return
endif
'Step 7 : first valid non-capture move
if fr = -1 then
fr = r ; fc = c ; tr = nr ; tc = nc
endif
endif
next
endif
next
next
'Step 8 : if no capture was found
if fr <> -1 then
do_move(fr, fc, tr, tc)
last_ai_fr = fr ; last_ai_fc = fc
last_ai_tr = tr ; last_ai_tc = tc
endif
current_turn = HUMAN
endfunc