Compare commits
4 Commits
633a8dedc8
...
0b13d9f027
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b13d9f027 | ||
|
|
e0bff7cb7e | ||
|
|
675e3ecc9d | ||
|
|
ffc60f8a06 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,5 +2,8 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|
||||||
|
examples/*
|
||||||
|
!examples/*.cent
|
||||||
|
|
||||||
.claude/
|
.claude/
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
112
cent
112
cent
@@ -1,16 +1,16 @@
|
|||||||
#! /home/nikolaj/.pyenv/shims/python
|
#! /usr/bin/env python
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
cent (-h|--help)
|
cent (-h|--help)
|
||||||
cent -i FILE
|
cent -i FILE
|
||||||
cent -c [--keep-c] FILE
|
cent -c [-k|--keep-c] FILE
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h --help Print this help screen
|
-h --help Print this help screen
|
||||||
-i Run the interpreter
|
-i Run the interpreter
|
||||||
-c Run the compiler
|
-c Run the compiler
|
||||||
--keep-c Keep the generated C file alongside the binary
|
-k --keep-c Keep the generated C file alongside the binary
|
||||||
FILE The file to compile/interpret
|
FILE The file to compile/interpret
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -27,56 +27,56 @@ from centvrion.ast_nodes import Program
|
|||||||
from centvrion.compiler.emitter import compile_program
|
from centvrion.compiler.emitter import compile_program
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
args = docopt(__doc__)
|
args = docopt(__doc__)
|
||||||
file_path = args["FILE"]
|
file_path = args["FILE"]
|
||||||
with open(file_path, "r", encoding="utf-8") as file_pointer:
|
with open(file_path, "r", encoding="utf-8") as file_pointer:
|
||||||
program_text = file_pointer.read() + "\n"
|
program_text = file_pointer.read() + "\n"
|
||||||
|
|
||||||
lexer = Lexer().get_lexer()
|
lexer = Lexer().get_lexer()
|
||||||
parser = Parser()
|
parser = Parser()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tokens = lexer.lex(program_text)
|
tokens = lexer.lex(program_text)
|
||||||
program = parser.parse(tokens)
|
program = parser.parse(tokens)
|
||||||
except LexingError as e:
|
except LexingError as e:
|
||||||
pos = e.source_pos
|
pos = e.source_pos
|
||||||
char = program_text[pos.idx] if pos.idx < len(program_text) else "?"
|
char = program_text[pos.idx] if pos.idx < len(program_text) else "?"
|
||||||
sys.exit(f"CENTVRION error: Invalid character {char!r} at line {pos.lineno}, column {pos.colno}")
|
sys.exit(f"CENTVRION error: Invalid character {char!r} at line {pos.lineno}, column {pos.colno}")
|
||||||
|
|
||||||
if isinstance(program, Program):
|
if isinstance(program, Program):
|
||||||
if args["-i"]:
|
if args["-i"]:
|
||||||
try:
|
try:
|
||||||
program.eval()
|
program.eval()
|
||||||
except CentvrionError as e:
|
except CentvrionError as e:
|
||||||
sys.exit(f"CENTVRION error: {e}")
|
sys.exit(f"CENTVRION error: {e}")
|
||||||
else:
|
|
||||||
c_source = compile_program(program)
|
|
||||||
runtime_c = os.path.join(
|
|
||||||
os.path.dirname(__file__),
|
|
||||||
"centvrion", "compiler", "runtime", "cent_runtime.c"
|
|
||||||
)
|
|
||||||
out_path = os.path.splitext(file_path)[0]
|
|
||||||
if args["--keep-c"]:
|
|
||||||
tmp_path = out_path + ".c"
|
|
||||||
with open(tmp_path, "w") as f:
|
|
||||||
f.write(c_source)
|
|
||||||
subprocess.run(
|
|
||||||
["gcc", "-O2", tmp_path, runtime_c, "-o", out_path],
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
with tempfile.NamedTemporaryFile(suffix=".c", delete=False, mode="w") as tmp:
|
|
||||||
tmp.write(c_source)
|
|
||||||
tmp_path = tmp.name
|
|
||||||
try:
|
|
||||||
subprocess.run(
|
|
||||||
["gcc", "-O2", tmp_path, runtime_c, "-o", out_path],
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
finally:
|
|
||||||
os.unlink(tmp_path)
|
|
||||||
else:
|
else:
|
||||||
raise Exception("Output not of type 'Program'", type(program))
|
c_source = compile_program(program)
|
||||||
|
runtime_c = os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
"centvrion", "compiler", "runtime", "cent_runtime.c"
|
||||||
|
)
|
||||||
|
out_path = os.path.splitext(file_path)[0]
|
||||||
|
if args["--keep-c"]:
|
||||||
|
tmp_path = out_path + ".c"
|
||||||
|
with open(tmp_path, "w") as f:
|
||||||
|
f.write(c_source)
|
||||||
|
subprocess.run(
|
||||||
|
["gcc", "-O2", tmp_path, runtime_c, "-o", out_path],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".c", delete=False, mode="w") as tmp:
|
||||||
|
tmp.write(c_source)
|
||||||
|
tmp_path = tmp.name
|
||||||
|
try:
|
||||||
|
subprocess.run(
|
||||||
|
["gcc", "-O2", tmp_path, runtime_c, "-o", out_path],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
os.unlink(tmp_path)
|
||||||
|
else:
|
||||||
|
raise Exception("Output not of type 'Program'", type(program))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ def frac_to_fraction(s, magnvm=False, svbnvlla=False):
|
|||||||
def fraction_to_frac(f, magnvm=False, svbnvlla=False):
|
def fraction_to_frac(f, magnvm=False, svbnvlla=False):
|
||||||
if f < 0:
|
if f < 0:
|
||||||
if not svbnvlla:
|
if not svbnvlla:
|
||||||
raise CentvrionError("Cannot display negative fractions without 'SVBNVLLA' module")
|
raise CentvrionError("Cannot display negative numbers without 'SVBNVLLA' module")
|
||||||
return "-" + fraction_to_frac(-f, magnvm, svbnvlla)
|
return "-" + fraction_to_frac(-f, magnvm, svbnvlla)
|
||||||
|
|
||||||
integer_part = int(f)
|
integer_part = int(f)
|
||||||
@@ -899,6 +899,9 @@ class BuiltIn(Node):
|
|||||||
if not isinstance(params[0], ValList):
|
if not isinstance(params[0], ValList):
|
||||||
raise CentvrionError("LONGITVDO requires an array")
|
raise CentvrionError("LONGITVDO requires an array")
|
||||||
return vtable, ValInt(len(params[0].value()))
|
return vtable, ValInt(len(params[0].value()))
|
||||||
|
case "EVERRO":
|
||||||
|
print("\033[2J\033[H", end="", flush=True)
|
||||||
|
return vtable, ValNul()
|
||||||
case _:
|
case _:
|
||||||
raise NotImplementedError(self.builtin)
|
raise NotImplementedError(self.builtin)
|
||||||
|
|
||||||
|
|||||||
@@ -187,6 +187,10 @@ def _emit_builtin(node, ctx):
|
|||||||
lines.append("break;")
|
lines.append("break;")
|
||||||
lines.append(f"CentValue {tmp} = cent_null();")
|
lines.append(f"CentValue {tmp} = cent_null();")
|
||||||
|
|
||||||
|
case "EVERRO":
|
||||||
|
lines.append("cent_everro();")
|
||||||
|
lines.append(f"CentValue {tmp} = cent_null();")
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
raise NotImplementedError(node.builtin)
|
raise NotImplementedError(node.builtin)
|
||||||
|
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ static int write_val(CentValue v, char *buf, int bufsz) {
|
|||||||
long num = v.fval.num, den = v.fval.den;
|
long num = v.fval.num, den = v.fval.den;
|
||||||
if (den < 0) { num = -num; den = -den; }
|
if (den < 0) { num = -num; den = -den; }
|
||||||
if (num < 0)
|
if (num < 0)
|
||||||
cent_runtime_error("cannot display negative fraction without SVBNVLLA");
|
cent_runtime_error("cannot display negative numbers without SVBNVLLA");
|
||||||
long int_part = num / den;
|
long int_part = num / den;
|
||||||
long rem_num = num % den;
|
long rem_num = num % den;
|
||||||
|
|
||||||
@@ -458,6 +458,11 @@ void cent_dice(CentValue v) {
|
|||||||
fputc('\n', stdout);
|
fputc('\n', stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cent_everro(void) {
|
||||||
|
fputs("\033[2J\033[H", stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
CentValue cent_avdi(void) {
|
CentValue cent_avdi(void) {
|
||||||
char *buf = cent_arena_alloc(cent_arena, 1024);
|
char *buf = cent_arena_alloc(cent_arena, 1024);
|
||||||
if (!fgets(buf, 1024, stdin)) {
|
if (!fgets(buf, 1024, stdin)) {
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ CentValue cent_avdi_numerus(void); /* AVDI_NVMERVS */
|
|||||||
CentValue cent_longitudo(CentValue v); /* LONGITVDO */
|
CentValue cent_longitudo(CentValue v); /* LONGITVDO */
|
||||||
CentValue cent_fortis_numerus(CentValue lo, CentValue hi); /* FORTIS_NVMERVS */
|
CentValue cent_fortis_numerus(CentValue lo, CentValue hi); /* FORTIS_NVMERVS */
|
||||||
CentValue cent_fortis_electionis(CentValue lst); /* FORTIS_ELECTIONIS */
|
CentValue cent_fortis_electionis(CentValue lst); /* FORTIS_ELECTIONIS */
|
||||||
|
void cent_everro(void); /* EVERRO */
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
/* Array helpers */
|
/* Array helpers */
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ builtin_tokens = [("BUILTIN", i) for i in [
|
|||||||
"AVDI_NVMERVS",
|
"AVDI_NVMERVS",
|
||||||
"AVDI",
|
"AVDI",
|
||||||
"DICE",
|
"DICE",
|
||||||
|
"EVERRO",
|
||||||
"FORTIS_NVMERVS",
|
"FORTIS_NVMERVS",
|
||||||
"FORTIS_ELECTIONIS",
|
"FORTIS_ELECTIONIS",
|
||||||
"LONGITVDO"
|
"LONGITVDO"
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
1
tests.py
1
tests.py
@@ -117,6 +117,7 @@ output_tests = [
|
|||||||
("DICE(\"a\", \"b\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("a"), String("b")]))]), ValStr("a b"), "a b\n"),
|
("DICE(\"a\", \"b\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("a"), String("b")]))]), ValStr("a b"), "a b\n"),
|
||||||
("DICE(\"line one\")\nDICE(\"line two\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("line one")])), ExpressionStatement(BuiltIn("DICE", [String("line two")]))]), ValStr("line two"), "line one\nline two\n"),
|
("DICE(\"line one\")\nDICE(\"line two\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("line one")])), ExpressionStatement(BuiltIn("DICE", [String("line two")]))]), ValStr("line two"), "line one\nline two\n"),
|
||||||
("DICE(DICE(II))", Program([], [ExpressionStatement(BuiltIn("DICE", [BuiltIn("DICE", [Numeral("II")])]))]), ValStr("II"), "II\nII\n"),
|
("DICE(DICE(II))", Program([], [ExpressionStatement(BuiltIn("DICE", [BuiltIn("DICE", [Numeral("II")])]))]), ValStr("II"), "II\nII\n"),
|
||||||
|
("EVERRO()", Program([], [ExpressionStatement(BuiltIn("EVERRO", []))]), ValNul(), "\033[2J\033[H"),
|
||||||
]
|
]
|
||||||
|
|
||||||
class TestOutput(unittest.TestCase):
|
class TestOutput(unittest.TestCase):
|
||||||
|
|||||||
Reference in New Issue
Block a user