summaryrefslogtreecommitdiff
path: root/tests/ep_to_epd.pl
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-16 00:08:27 -0700
committerScott Gasch <[email protected]>2026-09-16 00:08:27 -0700
commitb91c8453f3fe21c8ccd3568923d8e39141c9c945 (patch)
treeb908fea282ecd4075744590d3dfc88ffa903d943 /tests/ep_to_epd.pl
parentc0ac3d7845f60c7f6f2c3bfc306ceb0e4ac045ba (diff)
Add search self-tests (hash consistency, QSearch sanity) and a native EPD parser (`epd` command + ParseEpdRecord), fixing a silent-corruption bug in convert_epd.pl that broke every Black-to-move EPD conversion.
testsearch.c: TestSearchHashConsistency exercises the hash table through real search (repeat-with-warm-table and shallow-then-deep agreement) rather than synthetic store/probe calls. TestQSearchSanity checks termination and a node-count ceiling; its own header documents that the TEST=1 self-test build doesn't define PERF_COUNTERS, so it checks whole-search node count rather than the qsearch-only counter. A planned null-move zugzwang test was dropped in favor of tests/zugzwang.epd (best-move outcome, not an internal counter) after the three classic literature positions turned out not to structurally reach this engine's verification-search code path on this build. fen.c/script.c/command.c: new `epd <file>` command reads raw EPD directly via ParseEpdRecord, reusing ScriptCommand's existing suite-reporting machinery (factored into _ProcessSuiteLine/_PrintSuiteReport). Replaces the need for tests/convert_epd.pl, which had a real bug: its bm/am regex's [^b]+ filler breaks on any Black-to-move record (side-to-move field is literally "b"), and unscoped $fen/$id/$solution globals meant a failed match silently duplicated the previous record instead of erroring. tests/ep_to_epd.pl converts the existing setboard/solution/avoid/id/go suites back to real EPD; cross-checked ecm_ringers.ep_ via `script` against ecm_ringers.epd via `epd` -- byte-identical node counts and solve stats. Added .epd siblings for every genuine tactical suite in tests/. Added src/TESTING_PLAN.md: full audit of test*.c coverage gaps (dead SEE oracle, unwired eval/recognizer tests, zero hash-table correctness coverage, thin search self-tests) with a prioritized plan for closing them. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'tests/ep_to_epd.pl')
-rw-r--r--tests/ep_to_epd.pl56
1 files changed, 56 insertions, 0 deletions
diff --git a/tests/ep_to_epd.pl b/tests/ep_to_epd.pl
new file mode 100644
index 0000000..f419a0a
--- /dev/null
+++ b/tests/ep_to_epd.pl
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+#
+# Reverse of convert_epd.pl: turns typhoon's setboard/solution|avoid/
+# id/go script-format suites back into real EPD, one record per line,
+# now that the engine can read EPD directly (`epd <file>` -- see
+# ParseEpdRecord in fen.c, EpdCommand in script.c). Written to retire
+# the setboard/solution/go intermediate format for suites that are
+# just EPD underneath, so there's one suite format instead of two.
+#
+# Mirrors ScriptCommand's own state machine: track the most recent
+# setboard/id/solution/avoid seen and emit one EPD record per "go",
+# ignoring any other command in between (sd, st, easy, version,
+# quit, ...) -- those belong to scripts that aren't really EPD suites
+# (bench.ep_, trial.ep_) and shouldn't be fed to this converter.
+use strict;
+use warnings;
+
+my $fen;
+my $id;
+my @sol;
+my @avoid;
+
+while (my $line = <>) {
+ chomp $line;
+ $line =~ s/\r$//;
+ if ($line =~ /^\s*setboard\s+(.*?)\s*$/i) {
+ $fen = $1;
+ } elsif ($line =~ /^\s*id\s+(.*?)\s*$/i) {
+ $id = $1;
+ $id =~ s/^"|"$//g;
+ } elsif ($line =~ /^\s*solution\s+(.*?)\s*$/i) {
+ push @sol, split(/\s+/, $1);
+ } elsif ($line =~ /^\s*avoid\s+(.*?)\s*$/i) {
+ push @avoid, split(/\s+/, $1);
+ } elsif ($line =~ /^\s*go\s*$/i) {
+ if (defined($fen) && (@sol || @avoid)) {
+ my ($opcode, @moves) = @sol ? ('bm', @sol) : ('am', @avoid);
+ print "$fen $opcode " . join(' ', @moves) . ';';
+ print " id \"$id\";" if defined $id;
+ print "\n";
+ } else {
+ warn "ep_to_epd.pl: skipping a 'go' with no fen/solution " .
+ "in scope (non-suite script line?)\n";
+ }
+ @sol = ();
+ @avoid = ();
+ $id = undef;
+ # $fen deliberately survives a record with no solution/avoid
+ # (mirrors ScriptCommand: a bare setboard+go with no solution
+ # just plays a move, it doesn't reset anything) -- but since we
+ # skip printing when there's no solution anyway, this only
+ # matters for a same-fen "go" that follows one with a solution,
+ # which none of the real suites in tests/ do.
+ }
+ # everything else (sd, st, easy, version, quit, ...) ignored.
+}