🐐 Connect 4 game
This commit is contained in:
282
examples/connect_iv.cent
Normal file
282
examples/connect_iv.cent
Normal file
@@ -0,0 +1,282 @@
|
||||
// Connect IV — Play against a perfect AI
|
||||
// Minimax with alpha-beta pruning + center-first move ordering
|
||||
// You are X (player I), AI is O (player II)
|
||||
// Enter column as Roman numeral I-VII
|
||||
|
||||
// Returns the bottommost empty row in col, or NVLLVS if full
|
||||
DEFINI find_slot(b, col) VT {
|
||||
DESIGNA ans VT NVLLVS
|
||||
DONICVM r VT I VSQVE VII FACE {
|
||||
SI b[(r - I) * VII + col] EST NVLLVS TVNC {
|
||||
DESIGNA ans VT r
|
||||
}
|
||||
}
|
||||
REDI(ans)
|
||||
}
|
||||
|
||||
// Returns VERITAS if player has four in a row
|
||||
DEFINI est_victor(b, player) VT {
|
||||
DONICVM r VT I VSQVE VII FACE {
|
||||
DONICVM c VT I VSQVE V FACE {
|
||||
DESIGNA idx VT (r - I) * VII + c
|
||||
SI b[idx] EST player ET b[idx + I] EST player ET b[idx + II] EST player ET b[idx + III] EST player TVNC {
|
||||
REDI(VERITAS)
|
||||
}
|
||||
}
|
||||
}
|
||||
DONICVM r VT I VSQVE IV FACE {
|
||||
DONICVM c VT I VSQVE VIII FACE {
|
||||
DESIGNA idx VT (r - I) * VII + c
|
||||
SI b[idx] EST player ET b[idx + VII] EST player ET b[idx + XIV] EST player ET b[idx + XXI] EST player TVNC {
|
||||
REDI(VERITAS)
|
||||
}
|
||||
}
|
||||
}
|
||||
DONICVM r VT I VSQVE IV FACE {
|
||||
DONICVM c VT I VSQVE V FACE {
|
||||
DESIGNA idx VT (r - I) * VII + c
|
||||
SI b[idx] EST player ET b[idx + VIII] EST player ET b[idx + XVI] EST player ET b[idx + XXIV] EST player TVNC {
|
||||
REDI(VERITAS)
|
||||
}
|
||||
}
|
||||
}
|
||||
DONICVM r VT I VSQVE IV FACE {
|
||||
DONICVM c VT IV VSQVE VIII FACE {
|
||||
DESIGNA idx VT (r - I) * VII + c
|
||||
SI b[idx] EST player ET b[idx + VI] EST player ET b[idx + XII] EST player ET b[idx + XVIII] EST player TVNC {
|
||||
REDI(VERITAS)
|
||||
}
|
||||
}
|
||||
}
|
||||
REDI(FALSITAS)
|
||||
}
|
||||
|
||||
DEFINI print_board(b) VT {
|
||||
DICE("+---+---+---+---+---+---+---+")
|
||||
DONICVM r VT I VSQVE VII FACE {
|
||||
DESIGNA line VT "| "
|
||||
DONICVM c VT I VSQVE VIII FACE {
|
||||
DESIGNA cell VT b[(r - I) * VII + c]
|
||||
SI cell EST I TVNC {
|
||||
DESIGNA line VT line & "X | "
|
||||
} ALVID SI cell EST II TVNC {
|
||||
DESIGNA line VT line & "O | "
|
||||
} ALVID {
|
||||
DESIGNA line VT line & ". | "
|
||||
}
|
||||
}
|
||||
DICE(line)
|
||||
}
|
||||
DICE("+---+---+---+---+---+---+---+")
|
||||
DICE(" I II III IV V VI VII")
|
||||
REDI(NVLLVS)
|
||||
}
|
||||
|
||||
// Score a 4-cell window: positive favours AI (II), negative favours player (I).
|
||||
// Only scores windows that are not mixed (one side can still win them).
|
||||
DEFINI score_fenestram(a, b, c, d) VT {
|
||||
DESIGNA ai VT NVLLVS
|
||||
DESIGNA pl VT NVLLVS
|
||||
SI a EST II TVNC { DESIGNA ai VT ai + I }
|
||||
SI b EST II TVNC { DESIGNA ai VT ai + I }
|
||||
SI c EST II TVNC { DESIGNA ai VT ai + I }
|
||||
SI d EST II TVNC { DESIGNA ai VT ai + I }
|
||||
SI a EST I TVNC { DESIGNA pl VT pl + I }
|
||||
SI b EST I TVNC { DESIGNA pl VT pl + I }
|
||||
SI c EST I TVNC { DESIGNA pl VT pl + I }
|
||||
SI d EST I TVNC { DESIGNA pl VT pl + I }
|
||||
SI pl EST NVLLVS TVNC {
|
||||
SI ai EST III TVNC { REDI(V) }
|
||||
SI ai EST II TVNC { REDI(II) }
|
||||
}
|
||||
SI ai EST NVLLVS TVNC {
|
||||
SI pl EST III TVNC { REDI(NVLLVS - V) }
|
||||
SI pl EST II TVNC { REDI(NVLLVS - II) }
|
||||
}
|
||||
REDI(NVLLVS)
|
||||
}
|
||||
|
||||
// Static board evaluation: scan all 69 windows of 4 cells.
|
||||
// Returns a score in roughly [-350, 350]; positive = AI advantage.
|
||||
DEFINI aestima(b) VT {
|
||||
DESIGNA score VT NVLLVS
|
||||
// Center column preference: each AI piece in column IV is worth +1
|
||||
DONICVM r VT I VSQVE VII FACE {
|
||||
SI b[(r - I) * VII + IV] EST II TVNC {
|
||||
DESIGNA score VT score + I
|
||||
}
|
||||
}
|
||||
// Horizontal windows (6 rows x 4 starting columns = 24)
|
||||
DONICVM r VT I VSQVE VII FACE {
|
||||
DONICVM c VT I VSQVE V FACE {
|
||||
DESIGNA idx VT (r - I) * VII + c
|
||||
DESIGNA score VT score + INVOCA score_fenestram(b[idx], b[idx + I], b[idx + II], b[idx + III])
|
||||
}
|
||||
}
|
||||
// Vertical windows (3 starting rows x 7 columns = 21)
|
||||
DONICVM r VT I VSQVE IV FACE {
|
||||
DONICVM c VT I VSQVE VIII FACE {
|
||||
DESIGNA idx VT (r - I) * VII + c
|
||||
DESIGNA score VT score + INVOCA score_fenestram(b[idx], b[idx + VII], b[idx + XIV], b[idx + XXI])
|
||||
}
|
||||
}
|
||||
// Diagonal up-right windows (3 starting rows x 4 starting columns = 12)
|
||||
DONICVM r VT I VSQVE IV FACE {
|
||||
DONICVM c VT I VSQVE V FACE {
|
||||
DESIGNA idx VT (r - I) * VII + c
|
||||
DESIGNA score VT score + INVOCA score_fenestram(b[idx], b[idx + VIII], b[idx + XVI], b[idx + XXIV])
|
||||
}
|
||||
}
|
||||
// Diagonal up-left windows (3 starting rows x 4 starting columns = 12)
|
||||
DONICVM r VT I VSQVE IV FACE {
|
||||
DONICVM c VT IV VSQVE VIII FACE {
|
||||
DESIGNA idx VT (r - I) * VII + c
|
||||
DESIGNA score VT score + INVOCA score_fenestram(b[idx], b[idx + VI], b[idx + XII], b[idx + XVIII])
|
||||
}
|
||||
}
|
||||
REDI(score)
|
||||
}
|
||||
|
||||
// Minimax with alpha-beta pruning and depth-limited aestima evaluation.
|
||||
// depth = remaining search plies (counts down from a fixed budget).
|
||||
// Wins score ±(M + depth) so the search always prefers quicker wins and
|
||||
// delays losses; aestima values stay well within ±M so wins dominate.
|
||||
DEFINI minimax(b, depth, alpha, beta, maxi) VT {
|
||||
SI maxi TVNC {
|
||||
SI INVOCA est_victor(b, I) TVNC {
|
||||
REDI(NVLLVS - M - depth)
|
||||
}
|
||||
} ALVID {
|
||||
SI INVOCA est_victor(b, II) TVNC {
|
||||
REDI(M + depth)
|
||||
}
|
||||
}
|
||||
SI depth EST I TVNC {
|
||||
REDI(INVOCA aestima(b))
|
||||
}
|
||||
DESIGNA col_order VT [IV, III, V, II, VI, I, VII]
|
||||
SI maxi TVNC {
|
||||
DESIGNA best VT NVLLVS - M - VII
|
||||
PER c IN col_order FACE {
|
||||
DESIGNA linea VT INVOCA find_slot(b, c)
|
||||
SI NON (linea EST NVLLVS) TVNC {
|
||||
DESIGNA b[(linea - I) * VII + c] VT II
|
||||
DESIGNA score VT INVOCA minimax(b, depth - I, alpha, beta, FALSITAS)
|
||||
DESIGNA b[(linea - I) * VII + c] VT NVLLVS
|
||||
SI score PLVS best TVNC {
|
||||
DESIGNA best VT score
|
||||
}
|
||||
SI best PLVS alpha TVNC {
|
||||
DESIGNA alpha VT best
|
||||
}
|
||||
SI beta MINVS alpha AVT beta EST alpha TVNC {
|
||||
ERVMPE
|
||||
}
|
||||
}
|
||||
}
|
||||
REDI(best)
|
||||
} ALVID {
|
||||
DESIGNA best VT M + VII
|
||||
PER c IN col_order FACE {
|
||||
DESIGNA linea VT INVOCA find_slot(b, c)
|
||||
SI NON (linea EST NVLLVS) TVNC {
|
||||
DESIGNA b[(linea - I) * VII + c] VT I
|
||||
DESIGNA score VT INVOCA minimax(b, depth - I, alpha, beta, VERITAS)
|
||||
DESIGNA b[(linea - I) * VII + c] VT NVLLVS
|
||||
SI score MINVS best TVNC {
|
||||
DESIGNA best VT score
|
||||
}
|
||||
SI best MINVS beta TVNC {
|
||||
DESIGNA beta VT best
|
||||
}
|
||||
SI beta MINVS alpha AVT beta EST alpha TVNC {
|
||||
ERVMPE
|
||||
}
|
||||
}
|
||||
}
|
||||
REDI(best)
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the best column for the AI (depth VI = 5 plies of lookahead)
|
||||
DEFINI ai_move(b) VT {
|
||||
DESIGNA col_order VT [IV, III, V, II, VI, I, VII]
|
||||
DESIGNA best_score VT NVLLVS - M - VII
|
||||
DESIGNA best_col VT IV
|
||||
PER c IN col_order FACE {
|
||||
DESIGNA linea VT INVOCA find_slot(b, c)
|
||||
SI NON (linea EST NVLLVS) TVNC {
|
||||
DESIGNA b[(linea - I) * VII + c] VT II
|
||||
DESIGNA score VT INVOCA minimax(b, VI, NVLLVS - M - VII, M + VII, FALSITAS)
|
||||
DESIGNA b[(linea - I) * VII + c] VT NVLLVS
|
||||
SI score PLVS best_score TVNC {
|
||||
DESIGNA best_score VT score
|
||||
DESIGNA best_col VT c
|
||||
}
|
||||
}
|
||||
}
|
||||
REDI(best_col)
|
||||
}
|
||||
|
||||
// --- Board setup ---
|
||||
DESIGNA board VT [I VSQVE XLIII]
|
||||
DONICVM i VT I VSQVE XLIII FACE {
|
||||
DESIGNA board[i] VT NVLLVS
|
||||
}
|
||||
|
||||
DESIGNA moves VT NVLLVS
|
||||
DESIGNA game_over VT FALSITAS
|
||||
|
||||
DICE("=== CONNECT IV ===")
|
||||
DICE("You are X. AI is O.")
|
||||
DICE("Enter column as Roman numeral (I-VII).")
|
||||
DICE("")
|
||||
|
||||
DVM game_over FACE {
|
||||
EVERRO()
|
||||
INVOCA print_board(board)
|
||||
|
||||
DICE("Your move:")
|
||||
DESIGNA col VT AVDI_NVMERVS()
|
||||
SI col PLVS VII TVNC {
|
||||
DICE("Invalid column! Enter I through VII.")
|
||||
CONTINVA
|
||||
}
|
||||
DESIGNA linea VT INVOCA find_slot(board, col)
|
||||
SI linea EST NVLLVS TVNC {
|
||||
DICE("Column full! Try another.")
|
||||
CONTINVA
|
||||
}
|
||||
DESIGNA board[(linea - I) * VII + col] VT I
|
||||
DESIGNA moves VT moves + I
|
||||
SI INVOCA est_victor(board, I) TVNC {
|
||||
INVOCA print_board(board)
|
||||
DICE("You win!")
|
||||
DESIGNA game_over VT VERITAS
|
||||
CONTINVA
|
||||
}
|
||||
SI moves EST XLII TVNC {
|
||||
INVOCA print_board(board)
|
||||
DICE("Draw!")
|
||||
DESIGNA game_over VT VERITAS
|
||||
CONTINVA
|
||||
}
|
||||
|
||||
DICE("AI is thinking...")
|
||||
DESIGNA col VT INVOCA ai_move(board)
|
||||
DESIGNA linea VT INVOCA find_slot(board, col)
|
||||
DICE("AI plays column " & col & ".")
|
||||
DESIGNA board[(linea - I) * VII + col] VT II
|
||||
DESIGNA moves VT moves + I
|
||||
SI INVOCA est_victor(board, II) TVNC {
|
||||
INVOCA print_board(board)
|
||||
DICE("AI wins!")
|
||||
DESIGNA game_over VT VERITAS
|
||||
CONTINVA
|
||||
}
|
||||
SI moves EST XLII TVNC {
|
||||
INVOCA print_board(board)
|
||||
DICE("Draw!")
|
||||
DESIGNA game_over VT VERITAS
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user