codingame_tools.puzzle_manager¶
puzzle_manager
¶
Local working-directory management for solving existing CodinGame puzzles--much simpler than
codingame_tools.contribution_manager (which authors/edits a contribution): exactly one file
(data/solution.src) is ever editable, so there is no git repository involved at all.
See CgPuzzleManager for import_/repair/diff/discard_local/submit/play/play_local/
status/delete--its module docstring covers the three-way state-split design in full.
play/play_local each have a resolve_*/*_one pair too (resolve_play_indices/
play_one, resolve_play_local_test_cases/play_local_one), for a caller (e.g. cg puzzle
play-server/cg puzzle play's own CLI handlers) that wants to act on each result as it comes
in rather than waiting for the whole batch.
codingame_tools.puzzle_manager.schema for the working directory's three manifest files
(CgPuzzleIdentity/CgPuzzleServerData/CgPuzzleData);
codingame_tools.puzzle_manager.statement_render for rendering the cached HTML statement
(CgPuzzleManager.load_statement_html) into display blocks, used by cg puzzle description;
codingame_tools.test_runner (a separate, package-agnostic package--not puzzle_manager's
own) for how play_local/VS Code debugging actually run a solution; and
codingame_tools.puzzle_manager.resolver for how a puzzle directory is located.
DATA_SUBDIR_NAME
module-attribute
¶
DATA_SUBDIR_NAME = 'data'
The puzzle's user-editable content (solution.src, puzzle-data.json) lives under a data/
subdirectory of the working directory root.
META_SUBDIR_NAME
module-attribute
¶
META_SUBDIR_NAME = '.meta'
Container for gitignored, server-derived cache (puzzle-server-data.json) and read-only
reference files (statement.html, stub_generator.cgstub)--none of it is user-managed state,
and none of it is expected to survive a fresh git clone into a different repo (see
CgPuzzleManager.repair, which reconstructs it from puzzle.json's stable puzzle_id).
Always paired with a .gitignore (see GITIGNORE_FILE_NAME) at the working directory root, so
it's never accidentally tracked by whatever project ends up tracking the rest of the working
directory.
SOLUTION_FILE_STEM
module-attribute
¶
SOLUTION_FILE_STEM = 'solution'
Stem of the one real, editable/submittable solution file, which lives in data/.
STATEMENT_FILE_NAME
module-attribute
¶
STATEMENT_FILE_NAME = 'statement.html'
Read-only reference copy of the puzzle's rendered problem statement (see
CgTestSessionQuestionDetails.statement), under .meta/--not user-managed state, so it
doesn't belong in data/; regenerated on every import_()/repair(), never read back or
diffed; purely for the solver's own convenience (e.g. to reread the problem without a network
round trip).
STUB_GENERATOR_FILE_NAME
module-attribute
¶
STUB_GENERATOR_FILE_NAME = 'stub_generator.cgstub'
Read-only reference copy of the puzzle's stub-generation script (see
CgTestSessionQuestionDetails.stub_generator), under .meta/--informational only; this
package doesn't interpret the stub-generator DSL to produce a real starter solution.src,
unlike codingame_tools.contribution_manager's Python-only trivial stub for authoring a new
contribution (see CgPuzzleManager.import_'s docstring).
TESTS_SUBDIR_NAME
module-attribute
¶
TESTS_SUBDIR_NAME = 'tests'
Name of the puzzle working directory's .meta/-relative test-cases subdirectory.
CG_PUZZLE_DIR_ENV_VAR
module-attribute
¶
CG_PUZZLE_DIR_ENV_VAR = 'CG_PUZZLE_DIR'
Environment variable that can override puzzle-dir discovery, same as an explicit
--puzzle-dir CLI flag (parsing/wiring that flag is the CLI layer's job--this module just
accepts the resolved explicit value).
DEFAULT_PUZZLE_SUBDIR_NAME
module-attribute
¶
DEFAULT_PUZZLE_SUBDIR_NAME = 'puzzle'
Name of the subdirectory of the current directory checked as a last-resort discovery step.
PUZZLE_IDENTITY_FILE_NAME
module-attribute
¶
PUZZLE_IDENTITY_FILE_NAME = 'puzzle.json'
Name of the puzzle working directory's identity/manifest file, at its root (a sibling of
data//.meta/)--its presence is what identifies a directory as a puzzle working directory
at all.
PUZZLE_SCHEMA_VERSION
module-attribute
¶
PUZZLE_SCHEMA_VERSION = 1
Current on-disk format version for a puzzle working directory, recorded in
CgPuzzleIdentity.schema_version so a future format change can detect and offer to migrate an
older working directory.
CgSubmissionReport
dataclass
¶
CgSubmissionReport(validator_shareable, extra_data=dict(), best_score=None, codingamer_id=None, submission_id=None, score=None, achievements_completed=None, shared=None, puzzle_progress=None, validators=None, achievements=None, feedback=None, _completed_time=Alias('completedTime', default=None))
Bases: JSONWizardX
The complete response to Report/findReportBySubmission: a report on a single puzzle submission's results.
CAUTION, confirmed live (2026-07-31, and again 2026-08-01 for a puzzle with no prior
submission at all--see best_score): calling findReportBySubmission right after
TestSession/submit can race server-side grading--every field below except
validator_shareable has been observed entirely absent (not merely null) in some
partial-report snapshot. All of them are therefore Optional here except
validator_shareable, the only field confirmed present in every observed case so far; a
report is only "done" once they're all populated. See
CgReportServiceHelper.find_report_by_submission_when_ready, which polls until that's
true (or a timeout elapses) instead of returning a partial report.
validator_shareable
instance-attribute
¶
validator_shareable
Whether this submission's validator results are eligible to be shared.
best_score
class-attribute
instance-attribute
¶
best_score = None
The codingamer's best-ever validator score for this puzzle, 0.0 to 100.0 (may be higher
than score if this submission wasn't their best attempt). Confirmed live (2026-08-01)
absent--not just this submission's own score, but this field specifically--for a puzzle
the codingamer had never before submitted, i.e. there's no historical "best" yet at the
moment this was polled. Absent while grading is still in progress--see the class
docstring.
codingamer_id
class-attribute
instance-attribute
¶
codingamer_id = None
The submitting codingamer's numeric ID. Absent while grading is still in progress--see the class docstring.
submission_id
class-attribute
instance-attribute
¶
submission_id = None
Numeric ID of the submission this report is for (matches the submission_id argument).
Absent while grading is still in progress--see the class docstring.
score
class-attribute
instance-attribute
¶
score = None
The submission's validator score, 0.0 to 100.0. Absent while grading is still in progress--see the class docstring.
achievements_completed
class-attribute
instance-attribute
¶
achievements_completed = None
Whether all achievements for this puzzle have been completed by the codingamer. Absent while grading is still in progress--see the class docstring.
shared
class-attribute
instance-attribute
¶
shared = None
Whether the codingamer has publicly shared their solution. Absent while grading is still in progress--see the class docstring.
puzzle_progress
class-attribute
instance-attribute
¶
puzzle_progress = None
Lightweight puzzle progress summary. Absent while grading is still in progress--see the class docstring.
validators
class-attribute
instance-attribute
¶
validators = None
Per-validator results for this submission. Absent while grading is still in progress--see the class docstring.
achievements
class-attribute
instance-attribute
¶
achievements = None
Achievements unlocked by this submission. Only observed as an empty list so far, so element shape is unknown otherwise. Absent while grading is still in progress--see the class docstring.
feedback
class-attribute
instance-attribute
¶
feedback = None
Community feedback/rating summary for the puzzle. Not confirmed to always be present (only a single example observed so far).
completed_time
property
writable
¶
completed_time
See the field docstring for _completed_time. Always UTC. None if not yet done.
is_ready
¶
is_ready()
Whether grading has finished--i.e. every field described as "absent while grading is
still in progress" above is now populated. See the class docstring and
CgReportServiceHelper.find_report_by_submission_when_ready.
Source code in codingame_tools/client/common/protocol/report.py
140 141 142 143 144 145 146 147 148 149 150 151 | |
CgPuzzleBuildFailedError
¶
CgPuzzleBuildFailedError(result)
Bases: CgPuzzleManagerError
Raised by CgPuzzleManager.play_local when build_solution() failed, so no test case was
run at all. Carries the full CgBuildResult (compiler diagnostics in .result.output) via
.result.
Note build_solution() itself does not raise this--it returns the result, so a caller
driving the build directly can display diagnostics however it likes. This exists for the
batch wrapper, which has no other way to say "nothing ran".
Source code in codingame_tools/puzzle_manager/manager.py
258 259 260 | |
CgPuzzleDiscardResult
dataclass
¶
CgPuzzleDiscardResult(code, solution_language)
The outcome of CgPuzzleManager.discard_local().
code
instance-attribute
¶
code
The server's last-submitted code, now also written to data/solution.src.
solution_language
instance-attribute
¶
solution_language
The language code is written in (the server's last submission may be in a different
language than data/puzzle-data.json's previously-recorded solution_language--this is
the fresh, now-authoritative value; discard_local() updates puzzle-data.json to
match).
CgPuzzleLocalTestFailedError
¶
CgPuzzleLocalTestFailedError(results)
Bases: CgPuzzleManagerError
Raised by CgPuzzleManager.play_local if any test case failed. Carries every result (not
just the failing ones) via .results, so a caller can report the full picture.
Source code in codingame_tools/puzzle_manager/manager.py
242 243 244 245 246 | |
CgPuzzleLocalTestResult
dataclass
¶
CgPuzzleLocalTestResult(index, label, passed, input, expected_output, actual_output, stderr, timed_out)
The outcome of running data/solution.src against one downloaded .meta/tests/ test
case--see CgPuzzleManager.play_local.
index
instance-attribute
¶
index
The test case's server-assigned index (see CgPuzzleDownloadedTestCase.index).
passed
instance-attribute
¶
passed
Whether the run completed without crashing/timing out and its stdout matched the test
case's expected output (see codingame_tools.test_runner.outputs_match).
stderr
instance-attribute
¶
stderr
What the solution wrote to stderr (not itself a failure condition, but useful context when a test does fail).
timed_out
instance-attribute
¶
timed_out
Whether the run was killed for exceeding its timeout rather than running to completion.
CgPuzzleManager
¶
CgPuzzleManager(puzzle_dir, client, *, toolchain_dir=None, mount_root=None, toolchain_languages=None, toolchain_image=None)
Builds/updates a puzzle working directory (puzzle_dir) against the server, via an
already-authenticated CgClient. See the module docstring for the (deliberately much
simpler than codingame_tools.contribution_manager) design this is backed by.
Source code in codingame_tools/puzzle_manager/manager.py
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | |
toolchain_dir
instance-attribute
¶
toolchain_dir = Path(toolchain_dir) if toolchain_dir is not None else default_global_data_dir() / TOOLCHAIN_SUBDIR_NAME
Per-user global directory holding user-tweakable per-language toolchain (container image)
definitions--see codingame_tools.language.CgLanguageContext.toolchain_dir. Global rather
than per-working-directory so one tweak applies everywhere. The CLI passes the value resolved
from config; the default keeps library/test use working with no config at all.
mount_root
instance-attribute
¶
mount_root = Path(mount_root).resolve() if mount_root is not None else None
Editor workspace root to bind-mount for containerized languages, or None to derive it (see
language_context). Normally VS Code's ${workspaceFolder}, passed through by the CLI: cg's
own find_workspace_root is a heuristic, and the editor knows the real answer.
identity_file
property
¶
identity_file
Path to this working directory's puzzle.json (stable identity) manifest.
server_data_file
property
¶
server_data_file
Path to this working directory's .meta/puzzle-server-data.json (gitignored cache).
tests_dir
property
¶
tests_dir
Path to this working directory's .meta/tests/ (downloaded test case input/output--see
codingame_tools.puzzle_manager.test_cases_dir).
solution_file
property
¶
solution_file
The one real solution file, data/solution.<ext>.
Resolved by looking for whatever is actually there rather than by deriving the name from
the recorded language: a working directory written by an older cg still has
solution.src, and the file that exists is the one the user has been editing.
solution_snapshot_file
property
¶
solution_snapshot_file
Path to .meta/solution-snapshot.json--see CgPuzzleSolutionSnapshot.
selected_test_file
property
¶
selected_test_file
Path to .meta/selected-test.json--see CgPuzzleSelectedTest.
puzzle_data_file
property
¶
puzzle_data_file
Path to this working directory's data/puzzle-data.json (user-editable metadata).
statement_file
property
¶
statement_file
Path to this working directory's .meta/statement.html (read-only reference copy of
the puzzle's rendered problem statement).
load_selected_test
¶
load_selected_test()
The explicitly selected test case, or None if none has been chosen.
Source code in codingame_tools/puzzle_manager/manager.py
462 463 464 465 466 | |
select_test
¶
select_test(test_index)
Choose which test case the debugger runs against.
Raises:
-
CgPuzzleManagerError–if no downloaded test case has that index--catching a typo now rather than at the moment a debug session fails to start.
Source code in codingame_tools/puzzle_manager/manager.py
468 469 470 471 472 473 474 475 476 477 478 479 480 481 | |
clear_selected_test
¶
clear_selected_test()
Forget the explicit selection, falling back to the default (the first test case).
Source code in codingame_tools/puzzle_manager/manager.py
483 484 485 | |
resolve_debug_test_index
¶
resolve_debug_test_index()
Which single test a debug session should use: the selection, else the first test case.
Defaulting rather than refusing is deliberate--debugging works immediately after an import, with no selection step, which is the common case.
Raises:
-
CgPuzzleManagerError–if there are no downloaded test cases at all.
Source code in codingame_tools/puzzle_manager/manager.py
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | |
load_identity
¶
load_identity()
Load puzzle.json, or None if this directory has never been imported.
Source code in codingame_tools/puzzle_manager/manager.py
518 519 520 521 522 | |
load_statement_html
¶
load_statement_html()
Read .meta/statement.html, or None if it doesn't exist (never imported, or .meta/
needs repair()).
Source code in codingame_tools/puzzle_manager/manager.py
524 525 526 527 528 529 | |
load_server_data
¶
load_server_data()
Load .meta/puzzle-server-data.json, or None if it's missing (needs repair()--e.g.
a fresh clone that (correctly) didn't bring gitignored .meta/ along).
Source code in codingame_tools/puzzle_manager/manager.py
531 532 533 534 535 536 | |
load_puzzle_data
¶
load_puzzle_data()
Load data/puzzle-data.json, or None if this directory has never been imported.
Source code in codingame_tools/puzzle_manager/manager.py
538 539 540 541 542 | |
load_solution
¶
load_solution()
Read data/solution.src.
Raises:
-
FileNotFoundError–if
solution.srcdoesn't exist.
Source code in codingame_tools/puzzle_manager/manager.py
569 570 571 572 573 574 575 | |
import_
async
¶
import_(puzzle_ref, *, language=None)
resolves puzzle_ref to a real
pretty ID (see _resolve_puzzle_ref--a numeric ID, a pretty ID, an exact title match,
or a case-insensitive title match, tried in that order), then resolves this
codingamer's test session for it (Puzzle/generateSessionFromPuzzlePrettyId), then
TestSession/startTestSession to fetch its current state.
What lands in data/solution.src depends on language:
language=None(the default): the codingamer's existing saved answer, in whatever language they last used (CgTestSessionQuestion.answer), or a placeholder in_DEFAULT_IMPORT_LANGUAGEif this puzzle has never been attempted at all.languagegiven: that language, seeded with the codingamer's most recent saved code for it (CodinGame keeps one per language--seeCgTestSessionService.get_previous_code_by_language_id), or a placeholder if they've never attempted this puzzle in it. Equivalent to importing and then callingset_language(), and it shares that code path.
A placeholder is a bare comment: this package does not interpret the puzzle's
stub-generator DSL to produce a real starter solution the way an IDE would;
.meta/stub_generator.cgstub (see below) is written as a read-only reference instead,
for the solver to consult by hand.
Also writes .meta/statement.html, .meta/stub_generator.cgstub, and .meta/tests/
(each test case's downloaded input/output--see
codingame_tools.puzzle_manager.test_cases_dir)--all read-only reference copies,
regenerated here, never read back or diffed--and refreshes the solution.<ext>
convenience symlink at the working directory root--see the module docstring for why
these live under .meta/ rather than data/.
Parameters:
-
puzzle_ref(str) –A general puzzle reference--numeric ID, pretty ID, exact title, or case-insensitive title (see
_resolve_puzzle_ref). -
language(CgSolutionLanguage | None, default:None) –Language to start in. Defaults to
None, meaning "whichever language the codingamer last used for this puzzle". When given, switches to it and restores any code already saved in it--see above.
Raises:
-
CgPuzzleManagerError–if this directory already tracks a puzzle, if
puzzle_refcouldn't be resolved to a real puzzle, or if the puzzle isn't a supported type (currently, only classic "PUZZLE_INOUT" puzzles).
Source code in codingame_tools/puzzle_manager/manager.py
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 | |
repair
async
¶
repair()
Reconstruct .meta/ (the test session handle, plus the read-only statement.html/
stub_generator.cgstub/tests/ reference copies) from puzzle.json's stable
puzzle_id--for recovering from .meta/ being missing, e.g. after a fresh clone into
a different repo (it's gitignored on purpose--see the module docstring) or manual
deletion/corruption. data/ (solution.src, puzzle-data.json) is never touched--
there's nothing to preserve from, since it's exactly the git-tracked content a clone
would have brought along.
Looks up Puzzle/findProgressByIds([puzzle_id]) for a fresh pretty_id/title, and
(if already available there) a reusable test_session_handle directly--otherwise
falls back to Puzzle/generateSessionFromPuzzlePrettyId using that fresh pretty_id.
Either way, cross-checks the resulting session's own reported puzzle ID against
puzzle_id before trusting anything else about it (see CgPuzzleServerData's
docstring for why a looked-up pretty_id specifically is never trusted un-verified).
Raises:
-
FileNotFoundError–if this working directory has never been imported (no
puzzle.json), ordata/solution.srcitself is missing (nothing on disk to refresh the solution symlink for/repair alongside). -
CgPuzzleManagerError–if
.meta/already exists (nothing to repair), or if a fresh lookup's own reported puzzle ID doesn't matchpuzzle_id(refuses rather than risk repairing with mismatched data).
Source code in codingame_tools/puzzle_manager/manager.py
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 | |
diff
async
¶
diff()
A unified text diff between the local data/solution.src and the server's current
last-submitted answer for this puzzle--empty if they're identical, or if there's no
local file/no server answer at all yet (nothing meaningful to diff in that case).
Raises:
-
FileNotFoundError–if this working directory has never been imported.
-
CgPuzzleManagerError–if
.meta/is missing (runrepair()first).
Source code in codingame_tools/puzzle_manager/manager.py
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 | |
load_solution_snapshot
¶
load_solution_snapshot()
What this client last wrote to data/solution.src, or None if unknown (never
written, or .meta/ predates the snapshot).
Source code in codingame_tools/puzzle_manager/manager.py
913 914 915 916 917 918 | |
set_language
async
¶
set_language(language, *, force=False)
Switch this working directory to a different language, restoring the codingamer's own most recent code for it.
CodinGame keeps your latest source per language for a puzzle, so switching is not
"throw away what you have and start over"--any solution you'd previously written in the
target language comes back (see
CgTestSessionService.get_previous_code_by_language_id). Only a language you have never
attempted gets a placeholder.
This changes local state only. The server's notion of your current language is not moved by fetching code (confirmed live--it's a pure read); it follows once you actually run a server-side test or submit in the new language.
Refuses when data/solution.src holds work the server doesn't have, since switching
overwrites it. Local edits are considered safe to discard when they match either the
server's saved code for the current language or the placeholder this package would
have generated for it--the latter matters because importing with an explicit language
you've never used writes a placeholder that was never saved server-side, which would
otherwise leave the working directory permanently unable to switch away.
Parameters:
-
language(CgSolutionLanguage) –CodinGame language ID to switch to, e.g. "C++" (see
CgSolutionLanguage). -
force(bool, default:False) –Switch even when local edits would be lost.
Returns:
-
CgPuzzleSetLanguageResult–A
CgPuzzleSetLanguageResult--checkfrom_serverto tell "your old solution is back" -
CgPuzzleSetLanguageResult–from "here's an empty starting point".
Raises:
-
FileNotFoundError–if this working directory has never been imported.
-
CgPuzzleManagerError–if
languageisn't one this client knows, if it's already the current language, or if local edits would be lost andforceis False.
Source code in codingame_tools/puzzle_manager/manager.py
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 | |
discard_local
async
¶
discard_local()
overwrite data/solution.src with the server's current
last-submitted answer for this puzzle (and update data/puzzle-data.json's
solution_language to match, in case the last submission was in a different language
than previously recorded), then refresh the solution.<ext> symlink. Purely a local
overwrite--no submission or other server-side side effect.
Raises:
-
FileNotFoundError–if this working directory has never been imported.
-
CgPuzzleManagerError–if
.meta/is missing (runrepair()first), or if this puzzle has never been submitted at all (nothing server-side to discard to).
Source code in codingame_tools/puzzle_manager/manager.py
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 | |
submit
async
¶
submit()
Submit the current local data/solution.src to the server for credit
(TestSession/submit), in data/puzzle-data.json's recorded solution_language,
then fetch and return the resulting results report
(Report/findReportBySubmission)--score, achievement completion, and per-validator
pass/fail.
Named submit(), not push() (unlike codingame_tools.contribution_manager's
git-vocabulary naming)--a puzzle working directory has two distinct server-side
persistence phases, not one: the test session's current answer (see play()'s
docstring--confirmed live to be silently updated by any TestSession/play call, not
just this method) and this method's actual graded submission. "Push" would suggest
the former; this method is unambiguously the latter.
CAUTION: unlike codingame_tools.contribution_manager's push(), this always
creates a new graded submission--there's no draft/private-staging concept for puzzle
solutions. See CgTestSessionService.submit's docstring for the (currently
unhandled) heavy-validation Cloudflare/524 timeout risk shared with contribution
submission.
The report is fetched via CgReportServiceHelper.find_report_by_submission_when_ready
rather than the plain find_report_by_submission, since calling the latter immediately
after submitting can race server-side grading--see CgSubmissionReport's class
docstring.
Returns:
-
CgSubmissionReport–The new submission's
CgSubmissionReport(its.submission_idis the same numeric -
CgSubmissionReport–ID
TestSession/submititself returns).
Raises:
-
FileNotFoundError–if this working directory has never been imported.
-
CgPuzzleManagerError–if
.meta/is missing (runrepair()first). -
TimeoutError–if grading hasn't finished within
find_report_by_submission_when_ready's default timeout.
Source code in codingame_tools/puzzle_manager/manager.py
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 | |
resolve_play_indices
¶
resolve_play_indices(test_indices=None)
Resolve which 1-based test indices play()/play_one() should run against:
test_indices if given, unchanged; otherwise every downloaded test case's index
(.meta/tests/, i.e. every test case this working directory actually knows about--NOT
necessarily every test case the puzzle has). No network access--for a caller that wants
to loop over play_one() itself (e.g. to display each result as it comes in, rather
than waiting for the whole batch--see play()), this is the piece that used to be
done implicitly inside play().
Raises:
-
FileNotFoundError–if this working directory has never been imported, or (only when
test_indicesis not given) has no downloaded test cases at all. -
CgPuzzleManagerError–if
.meta/is missing (runrepair()first).
Source code in codingame_tools/puzzle_manager/manager.py
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 | |
play_one
async
¶
play_one(index)
Run the current local data/solution.src against a single one of the puzzle's test
cases via the server (TestSession/play--the IDE's "Test"/"Run" button, as opposed
to submit()'s full "Submit"). One live API call.
CONFIRMED LIVE (2026-08-01): this call has a side effect beyond just running the given
test case--the server durably persists whatever code was sent as the test session's
current answer (the same "current answer" returned by TestSession/startTestSession,
and visible in the web IDE from any browser), whether or not the test case actually
passes. This is NOT a grading/submission event (no Report/score is produced), and
there's no separate "just save, don't run" call--the web IDE itself has no autosave
either (confirmed: editing code there without running a test, then navigating away,
prompts "All changes will be lost")--so running at least one test case is, in effect,
the only way to persist a change short of a real submission. submit() also persists
the code this way (again regardless of whether the submission scores well), as a side
effect of grading it.
Parameters:
-
index(int) –1-based index to run against (see
CgTestSessionTestCase.index). Need not be locally downloaded--the server runs by index alone.
Returns:
-
CgPuzzleRemoteTestResult–The
CgPuzzleRemoteTestResultfor this index.
Raises:
-
FileNotFoundError–if this working directory has never been imported.
-
CgPuzzleManagerError–if
.meta/is missing (runrepair()first).
Source code in codingame_tools/puzzle_manager/manager.py
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 | |
play
async
¶
play(test_indices=None)
Run the current local data/solution.src against one or more of the puzzle's test
cases via the server (TestSession/play). Convenience batch wrapper around
play_one()--each index is a separate live API call (there is no batch form of
TestSession/play), run sequentially, in the order given; see play_one()'s docstring
for the shared side-effect caveat.
A caller that wants to display/act on each result as soon as it's available, rather
than waiting for every index to finish first, should call resolve_play_indices() and
play_one() directly in its own loop instead of this method (see cg puzzle
play-server's CLI implementation for exactly that).
Parameters:
-
test_indices(list[int] | None, default:None) –1-based indices to run against (see
CgTestSessionTestCase.index). Need not be locally downloaded--the server runs by index alone. If not given, runs every downloaded test case (.meta/tests/)--seeresolve_play_indices().
Returns:
-
list[CgPuzzleRemoteTestResult]–One
CgPuzzleRemoteTestResultper index, in the order run.
Raises:
-
FileNotFoundError–if this working directory has never been imported, or (only when
test_indicesis not given) has no downloaded test cases at all. -
CgPuzzleManagerError–if
.meta/is missing (runrepair()first).
Source code in codingame_tools/puzzle_manager/manager.py
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 | |
resolve_play_local_test_cases
¶
resolve_play_local_test_cases(test_indices=None)
Resolve which downloaded test cases play_local()/play_local_one() should run
against: the downloaded test cases matching test_indices, in the order given, if
given; otherwise every downloaded test case (.meta/tests/). No subprocess execution
--for a caller that wants to loop over play_local_one() itself (e.g. to display each
result as it comes in, rather than waiting for the whole batch--see play_local()),
this is the piece that used to be done implicitly inside play_local().
Raises:
-
FileNotFoundError–if this working directory has never been imported, or has no downloaded test cases at all (run
cg puzzle repairfirst). -
CgPuzzleManagerError–if
test_indicescontains an index with no downloaded test case.
Source code in codingame_tools/puzzle_manager/manager.py
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 | |
language_context
¶
language_context(solution_language=None, *, mount_root=None)
Describe this working directory to codingame_tools.language--see CgLanguageContext.
mount_root is what a containerized language bind-mounts. It defaults to the enclosing
VS Code workspace root, so that in-container paths equal host paths and one container
serves the whole workspace; pass it explicitly (VS Code's ${workspaceFolder}) when the
real workspace is known, since find_workspace_root is only a guess.
Infallible by design: never reads puzzle.json, never needs the directory to have been
imported. solution_language is accepted for signature stability but no longer selects
a path: there is one real solution file and solution_file finds it whatever extension
it carries.
Source code in codingame_tools/puzzle_manager/manager.py
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 | |
provision_vscode
async
¶
provision_vscode(*, workspace_root=None, force=False, check=False, debug_adapter_logging=False)
Generate this working directory's VS Code run/debug configuration, if its language has any, and write it into the workspace.
What's generated is the same for every working directory of that language, so this is run once per language rather than once per directory, and nothing here goes stale when test cases or the solution language change.
Parameters:
-
workspace_root(Path | None, default:None) –Where
.vscode/goes. Defaults tofind_workspace_root()--VS Code readslaunch.jsononly from the workspace root, which is often not this working directory (seecodingame_tools.language.vscode). -
force(bool, default:False) –Overwrite an existing config file that isn't strict JSON (i.e. uses JSONC comments) instead of refusing.
-
debug_adapter_logging(bool, default:False) –Generate a configuration that logs the debug adapter's own protocol exchange--see `CgVsCodeRequest`. -
check(bool, default:False) –Report what would change without touching anything. This is how staleness is detected: generated entries carry no version stamp, so "would rewriting change anything?" is the whole question, and it stays correct when a future release alters what gets generated.
Returns:
-
list[Path]–Every path that changed (or, under
check, would change), in write order. Empty means -
list[Path]–already up to date, or that this language has no VS Code integration.
Raises:
-
FileNotFoundError–if this working directory has never been imported.
-
CgVsCodeMergeError–if an existing config file can't be safely merged into.
Source code in codingame_tools/puzzle_manager/manager.py
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 | |
start_debug_session
async
¶
start_debug_session(test_index, *, timeout=DEFAULT_BUILD_TIMEOUT_SECONDS)
Get the solution ready for a debugger to attach to, fed by test case test_index's
input--see codingame_tools.language.CgLanguage.start_debug_session.
Only meaningful for a language whose debugger attaches to a running target (C++ via gdbserver). Python3's debugger launches the program itself, so it never calls this.
Raises:
-
FileNotFoundError–if this working directory has never been imported.
-
CgPuzzleManagerError–if there's no downloaded test case with that index.
-
CgLanguageOperationNotSupportedError–if this language has no attach-style debugging.
Source code in codingame_tools/puzzle_manager/manager.py
1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 | |
stop_debug_session
async
¶
stop_debug_session()
Tear down whatever start_debug_session() started. Safe to call when nothing is
running.
Source code in codingame_tools/puzzle_manager/manager.py
1343 1344 1345 1346 1347 1348 1349 1350 | |
build_solution
async
¶
build_solution(*, profile='run', timeout=DEFAULT_BUILD_TIMEOUT_SECONDS)
Build data/solution.src for local execution, if its language needs building at all
(Python3 doesn't--this is then an immediate no-op success).
A separate step from play_local_one() so a caller can display build diagnostics apart
from program output, report a compile error once rather than once per test case, and give
building its own generous timeout. Cheap to call repeatedly: an unchanged source since the
last successful build returns up_to_date=True having done nothing.
play_local() calls this for you. A caller driving play_local_one() itself (as
cg puzzle play does, to stream results) must call this first.
Returns:
-
CgBuildResult–A
CgBuildResult--check.ok; a build failure is reported, never raised.
Raises:
-
FileNotFoundError–if this working directory has never been imported.
Source code in codingame_tools/puzzle_manager/manager.py
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 | |
play_local_one
async
¶
play_local_one(test_case, *, timeout=DEFAULT_RUN_TIMEOUT_SECONDS)
Run the current local data/solution.src against a single downloaded test case
entirely locally--no network access at all, unlike play_one()--by shelling out to
the appropriate interpreter/compiler as a subprocess (see
codingame_tools.language.CgLanguage.run) and comparing captured stdout to the test
case's expected output.txt.
Never raises just because the test failed (crashed, timed out, or mismatched)--that's
reflected in the returned result's passed, same spirit as codingame_tools.
contribution_manager.manager.CgContributionManager.run_local_test. See play_local(),
which raises CgPuzzleLocalTestFailedError if any of a batch failed.
Does not build. For a language that needs compiling, call build_solution() first
(play_local() does this for you); this method only runs the already-built artifact.
For stepping through solution.src in a debugger against a specific test case's input
instead, see codingame_tools.test_runner.debug_stdin (launched directly, not through
this method--a subprocess like this one spawns can't be stepped into).
Parameters:
-
test_case(CgPuzzleDownloadedTestCase) –Which downloaded test case to run (see
resolve_play_local_test_cases()). -
timeout(float, default:DEFAULT_RUN_TIMEOUT_SECONDS) –Wall-clock timeout in seconds--see
codingame_tools.language. DEFAULT_RUN_TIMEOUT_SECONDS.
Returns:
-
CgPuzzleLocalTestResult–The outcome--see
CgPuzzleLocalTestResult.
Raises:
-
FileNotFoundError–if this working directory has never been imported.
-
CgLanguageOperationNotSupportedError–if
data/puzzle-data.json'ssolution_languageisn't yet supported bycodingame_tools. language.
Source code in codingame_tools/puzzle_manager/manager.py
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 | |
play_local
async
¶
play_local(test_indices=None, *, timeout=DEFAULT_RUN_TIMEOUT_SECONDS, build_timeout=DEFAULT_BUILD_TIMEOUT_SECONDS)
Run the current local data/solution.src against the downloaded .meta/tests/ test
cases entirely locally--no network access at all, unlike play(). Convenience batch
wrapper that calls build_solution() once and then loops play_local_one()
sequentially, in the order given.
A caller that wants to display/act on each result as soon as it's available, rather
than waiting for every test case to finish first, should call build_solution(),
resolve_play_local_test_cases() and play_local_one() directly in its own loop
instead of this method (see cg puzzle play's CLI implementation for exactly that).
Parameters:
-
test_indices(list[int] | None, default:None) –If given, only run the downloaded test cases with these indices (the same numbering
.meta/tests/'s directory names andplay()'s owntest_indicesuse), run in the order given. Defaults to running every downloaded test case--seeresolve_play_local_test_cases(). -
timeout(float, default:DEFAULT_RUN_TIMEOUT_SECONDS) –Per-test-case wall-clock timeout in seconds--see
codingame_tools.language.DEFAULT_RUN_TIMEOUT_SECONDS. -
build_timeout(float, default:DEFAULT_BUILD_TIMEOUT_SECONDS) –Wall-clock timeout for the one-time build step--see
codingame_tools.language.DEFAULT_BUILD_TIMEOUT_SECONDS.
Returns:
-
list[CgPuzzleLocalTestResult]–One
CgPuzzleLocalTestResultper test case run, in the order run.
Raises:
-
FileNotFoundError–if this working directory has never been imported, or has no downloaded test cases at all (run
cg puzzle repairfirst). -
CgPuzzleManagerError–if
test_indicescontains an index with no downloaded test case. -
CgPuzzleBuildFailedError–if the solution failed to build--carries the build output.
-
CgLanguageOperationNotSupportedError–if
data/puzzle-data.json'ssolution_languageisn't yet supported bycodingame_tools. language. -
CgPuzzleLocalTestFailedError–if any test case's output didn't match (or the solution crashed/timed out)--carries every result via
.results.
Source code in codingame_tools/puzzle_manager/manager.py
1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 | |
status
async
¶
status(*, refresh=False)
A point-in-time summary of this working directory--see CgPuzzleStatus.
By default, entirely local/cheap: no network access at all--just the three on-disk
manifests. Pass refresh=True to also check local_dirty (a live
TestSession/startTestSession call, same as diff()) and fetch progress (a live
Puzzle/findProgressByIds call)--both stay None otherwise. Unlike
codingame_tools.contribution_manager's status(), there is no cache file this writes
to for next time--puzzle working directories have no such cache at all (see the module
docstring); every refresh=True call is genuinely live, every time.
Parameters:
-
refresh(bool, default:False) –If True, also check for local edits against the server's last-submitted answer and fetch live progress/score info. Defaults to False.
Raises:
-
FileNotFoundError–if this working directory has never been imported.
-
CgPuzzleManagerError–if
.meta/is missing (runrepair()first).
Source code in codingame_tools/puzzle_manager/manager.py
1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 | |
delete
async
¶
delete()
Remove this working directory entirely (puzzle.json, .meta/, data/, and the
solution.<ext> convenience symlink)--purely local. Unlike codingame_tools.
contribution_manager.CgContributionManager.delete(), there is no server-side
counterpart at all here--a puzzle already exists on the server before you can solve
it (see the module docstring), so there is nothing to delete there; this only ever
removes your own local working directory.
No confirmation prompt here--that's the CLI's job (cg puzzle delete), same as every
other method in this class.
Raises:
-
FileNotFoundError–if this working directory has never been imported.
Source code in codingame_tools/puzzle_manager/manager.py
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 | |
CgPuzzleManagerError
¶
Bases: Exception
Raised for puzzle-manager-level errors not better represented by a more specific
exception (e.g. importing an unsupported puzzle type, discarding local edits when nothing
has ever been submitted to discard to, or a repair() whose fresh lookup didn't actually
match the puzzle it was supposed to repair).
CgPuzzleRemoteTestResult
dataclass
¶
CgPuzzleRemoteTestResult(index, label, result)
The outcome of playing one of a puzzle's test cases against the server
(TestSession/play)--see CgPuzzleManager.play.
label
instance-attribute
¶
label
The test case's real label, from .meta/tests/<index>/ if it's been downloaded--a
generic f"test {index}" placeholder otherwise (play() doesn't require an index to be
locally downloaded; the server doesn't need that to run it).
CgPuzzleSetLanguageResult
dataclass
¶
CgPuzzleSetLanguageResult(language, previous_language, code, from_server)
The outcome of CgPuzzleManager.set_language().
from_server
instance-attribute
¶
from_server
True when code is the codingamer's real saved work for language, restored from the
server; False when they had never attempted this puzzle in that language and code is just
a generated placeholder. Worth surfacing--the difference is invisible in the file itself,
and "your old solution is back" and "here's an empty starting point" are very different
things to be told.
CgPuzzleStatus
dataclass
¶
CgPuzzleStatus(puzzle_dir, puzzle_id, puzzle_handle, title, puzzle_pretty_id, puzzle_type, difficulty, solution_language, local_dirty, progress)
A point-in-time summary of a puzzle working directory--see CgPuzzleManager.status().
Much simpler than codingame_tools.contribution_manager.CgContributionStatus--no
versioning, no draft/moderation gate, no sync-state machine--matching this whole package's
"much simpler than contribution_manager" design (see the module docstring).
puzzle_handle
instance-attribute
¶
puzzle_handle
Opaque handle for the puzzle (CgPuzzleIdentity.puzzle_handle).
title
instance-attribute
¶
title
.meta/puzzle-server-data.json's cached title--informational only, may be stale (see
CgPuzzleServerData.title's docstring).
puzzle_pretty_id
instance-attribute
¶
puzzle_pretty_id
.meta/puzzle-server-data.json's cached pretty ID/slug--informational only, may be stale
(see CgPuzzleServerData.puzzle_pretty_id's docstring--never trusted as ground truth by
this package itself either).
puzzle_type
instance-attribute
¶
puzzle_type
.meta/puzzle-server-data.json's cached contribution type (e.g. "PUZZLE_INOUT"), or
None for a cache file written before this field existed (see CgPuzzleServerData.
puzzle_type)--run cg puzzle repair (after deleting .meta/) to populate it.
difficulty
instance-attribute
¶
difficulty
.meta/puzzle-server-data.json's cached difficulty level (e.g. "easy"), or None for a
cache file written before this field existed (see CgPuzzleServerData.difficulty)--same
backfill note as puzzle_type.
solution_language
instance-attribute
¶
solution_language
data/puzzle-data.json's solution_language--the language data/solution.src is
currently written in.
local_dirty
instance-attribute
¶
local_dirty
Whether data/solution.src currently differs from the server's last-submitted answer for
this puzzle (bool(diff()))--None unless status(refresh=True) checked (a live
TestSession/startTestSession call; there is no local cache of the server's answer to
compare against, unlike codingame_tools.contribution_manager).
progress
instance-attribute
¶
progress
This codingamer's live progress/score summary for the puzzle (Puzzle/findProgressByIds--
level/validator_score/solved_count/attempt_count/xp_points/last_activity), or
None unless status(refresh=True) fetched it.
CgPuzzleDirInferenceError
¶
Bases: Exception
Raised by infer_puzzle_dir when target_file doesn't resolve into a puzzle working
directory.
CgPuzzleDirNotFoundError
¶
CgPuzzleDirNotFoundError()
Bases: Exception
Raised by resolve_puzzle_dir() (unless allow_default=True) when no puzzle working
directory could be located by any discovery step. Does not indicate a bug--this is the
normal outcome before a puzzle has been imported in the current directory.
Source code in codingame_tools/puzzle_manager/resolver.py
43 44 45 46 47 48 | |
CgPuzzleData
dataclass
¶
CgPuzzleData(solution_language, extra_data=dict())
Bases: JSONWizardX
The data/puzzle-data.json manifest: the one piece of metadata that genuinely travels
with a solution submission (alongside data/solution.src itself), as opposed to read-only
puzzle content (statement, stub generator--see .meta/) or server-assigned identity/cache.
Safe to commit to git, same as solution.src.
solution_language
instance-attribute
¶
solution_language
The language data/solution.src is currently written in--submitted alongside the code on
submit()/play(), and so genuinely part of the user-managed submission state, not read-only
reference material or server-derived cache.
CgPuzzleIdentity
dataclass
¶
CgPuzzleIdentity(schema_version, puzzle_id, puzzle_handle, extra_data=dict())
Bases: JSONWizardX
The puzzle.json manifest: this working directory's stable identity, written once by
import_() and never changed afterward. Deliberately the only thing this package
considers safe to treat as permanent, git-trackable truth about which puzzle this is--see
the module docstring, and codingame_tools.puzzle_manager.manager's, for why
test_session_handle/title/puzzle_pretty_id are cache (.meta/) instead, not
identity.
schema_version
instance-attribute
¶
schema_version
The on-disk format version this working directory was written in--see
PUZZLE_SCHEMA_VERSION.
puzzle_id
instance-attribute
¶
puzzle_id
Numeric ID of the puzzle (CgTestSessionPuzzle.id)--the actual repair root key: the only
confirmed API that can regenerate everything else from scratch,
Puzzle/findProgressByIds, takes this, not puzzle_handle (no known API accepts the
opaque handle as a lookup key) or puzzle_pretty_id (not trusted as stable--see
CgPuzzleServerData.puzzle_pretty_id).
puzzle_handle
instance-attribute
¶
puzzle_handle
Opaque handle for the puzzle (CgTestSessionPuzzle.handle). Recorded here as part of this
working directory's permanent identity even though nothing can look a puzzle up by it
today--puzzle_id is what repair() actually queries with.
CgPuzzleServerData
dataclass
¶
CgPuzzleServerData(test_session_handle, title, puzzle_pretty_id, extra_data=dict(), puzzle_type=None, difficulty=None)
Bases: JSONWizardX
The .meta/puzzle-server-data.json manifest: cached, gitignored, re-derivable-from-
puzzle_id server state. Rebuilt by repair() whenever missing (e.g. after a fresh clone
into a different repo, or manual deletion/corruption).
test_session_handle
instance-attribute
¶
test_session_handle
This codingamer's test session handle for the puzzle (see
CgPuzzleService.generate_session_from_puzzle_pretty_id). Freely cached and reused
indefinitely, unlike puzzle_pretty_id/title below--confirmed (2026-07-30, per repeated
identical results from generateSessionFromPuzzlePrettyId) to be a per-user singleton with
affinity to the puzzle, not to whichever pretty_id happened to be used to generate it;
there is no known scenario where a cached handle here would need re-verification the way a
cached puzzle_pretty_id does.
title
instance-attribute
¶
title
Display title of the puzzle. Purely informational (e.g. for cg puzzle where output).
puzzle_pretty_id
instance-attribute
¶
puzzle_pretty_id
The puzzle's pretty ID/slug at the time this was last (re)written--informational only,
never trusted as ground truth. Unlike test_session_handle, a pretty ID is not
confirmed stable (it plausibly changes if the puzzle's title changes, and even a
structurally-valid pretty ID string could in principle end up reassigned to a different
puzzle over time)--so this cached copy is never fed back into an API call (e.g.
generateSessionFromPuzzlePrettyId) by this package. Whenever a pretty ID is actually
needed operationally (only repair() ever needs one, and only if findProgressByIds
didn't already hand back a reusable test_session_handle directly), it's re-derived fresh
from Puzzle/findProgressByIds(puzzle_id) and cross-checked against puzzle_id first--see
CgPuzzleManager.repair.
puzzle_type
class-attribute
instance-attribute
¶
puzzle_type = None
The puzzle's contribution type (e.g. "PUZZLE_INOUT"--currently the only type this package
supports at all, so always that value in practice today), as of when this was last (re)
written. Purely informational, same caching spirit as title/puzzle_pretty_id. None
for a cache file written before this field existed--not re-backfilled automatically; run
cg puzzle repair (after deleting .meta/) to populate it.
difficulty
class-attribute
instance-attribute
¶
difficulty = None
The puzzle's difficulty level (CgTestSessionPuzzle.level/CgLastActivityPuzzle.level,
e.g. "easy", "medium", "hard"), as of when this was last (re)written. Purely informational,
same caching spirit as title/puzzle_pretty_id. None for a cache file written before
this field existed--same backfill note as puzzle_type.
CgStatementBlock
dataclass
¶
CgStatementBlock(kind, text)
A single rendered block of a puzzle's HTML statement--see parse_statement_html.
kind
instance-attribute
¶
kind
One of "header" (a section title, e.g. "Goal"/"Input"/"Output"/"Constraints"/"Example"), "text" (ordinary paragraph/description text--may itself contain internal blank lines, e.g. the Goal section's multiple paragraphs), "example_input", or "example_output" (the literal, whitespace-preserved example test-case text under an "Example" section).
text
instance-attribute
¶
text
The block's rendered plain text (HTML tags stripped, entities unescaped).
find_solution_file
¶
find_solution_file(data_dir, extension=None)
The existing solution file in data_dir, whatever extension it currently carries.
Callers generally know the language and so know the name, but not always: a working
directory whose language changed out from under it, or one written by an older cg that used
a fixed solution.src, still has to be found. The expected name wins when present, so a
stray leftover can never shadow the real file; otherwise a lone solution.* is accepted.
Returns None if there is no solution file, or if several exist with no way to choose--the caller decides whether that's an error or a thing to repair.
Source code in codingame_tools/puzzle_manager/layout.py
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | |
solution_file_name
¶
solution_file_name(extension)
solution.<ext> for a known language extension, else solution.src.
The file carries the language's real extension rather than a fixed one because every tool
that reads it--language servers, debuggers, the compiler--dispatches on the extension. cg
previously kept data/solution.src fixed and maintained a solution.<ext> symlink beside
it, which cost a day of debugging: the debug info named one path, the editor resolved the
other, and breakpoints silently failed to bind. One real file with the right name has no
such gap, and needs no symlink support from the filesystem (which Windows only grants with
developer mode enabled).
Source code in codingame_tools/puzzle_manager/layout.py
70 71 72 73 74 75 76 77 78 79 80 | |
find_puzzle_dir
¶
find_puzzle_dir(explicit=None, *, settings=None, start_dir=None)
Locate the puzzle working directory to use, following the documented discovery precedence:
1. `explicit` (typically the resolved value of a `--puzzle-dir` CLI flag), if given.
2. The `CG_PUZZLE_DIR` environment variable, if set.
3. `settings.current_puzzle_dir`--the *active* working directory, set by
`cg puzzle import`/`create` and `cg puzzle activate`. Outranks the configured default
below so that creating a working directory somewhere isn't silently overridden by a
standing `puzzle_dir` preference pointing elsewhere.
4. `settings.puzzle_dir` (see `CgSettings.puzzle_dir`), if given and set.
5. `start_dir` (or the current directory, if not given), if it contains a `puzzle.json`.
6. `start_dir / "puzzle"`, if it contains a `puzzle.json`.
Steps 1-4 are taken at face value--the resolved directory need not contain a puzzle.json
yet (e.g. before the first cg puzzle import). Steps 5-6 are implicit inference and are
deliberately conservative: they only match if a puzzle.json is actually already there.
Returns:
-
Path | None–The resolved puzzle directory path, or None if nothing was found at all. This function
-
Path | None–never creates anything.
Source code in codingame_tools/puzzle_manager/resolver.py
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | |
infer_puzzle_dir
¶
infer_puzzle_dir(target_file)
Infer a puzzle working directory's root from a solution file somewhere within it--e.g. VS
Code's ${file} macro, however many symlink hops away from data/solution.src it might
be (a puzzle working directory's own solution.<ext> convenience symlink, or some other
symlink elsewhere entirely that a user set up themselves--see codingame_tools.
puzzle_manager.manager's module docstring). The only two things ever promised about
target_file: a debugger's breakpoints bind to whatever path was actually open in the
editor (so this function must not need that path to be anything in particular), and
resolving every symlink in it always eventually lands on data/solution.src.
So this isn't a search: fully resolving target_file (following every symlink to its real
target) always lands on <root>/data/solution.src--DATA_SUBDIR_NAME/SOLUTION_FILE_NAME
are fixed constants, not configurable--so <root> is deterministically two path segments
up from there. Confirmed by requiring puzzle.json to actually exist at that root, so a
target_file that isn't part of any puzzle working directory at all fails clearly rather
than returning a nonsense path.
Raises:
-
CgPuzzleDirInferenceError–if
target_file, once fully resolved, isn't.../data/solution.src, orpuzzle.jsonisn't present at the inferred root.
Source code in codingame_tools/puzzle_manager/resolver.py
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | |
resolve_puzzle_dir
¶
resolve_puzzle_dir(explicit=None, *, settings=None, start_dir=None, allow_default=False)
Locate the puzzle working directory, following the discovery precedence documented on
find_puzzle_dir.
If allow_default is True and no directory can be found, falls back to
start_dir / "puzzle" (or ./puzzle under the current directory)--useful for cg puzzle
import, which is happy to treat "nothing found" as "start a fresh working directory
there". Deliberately not bare start_dir/cwd itself--unlike a contribution working
directory (whose own import always requires an explicit target directory, so its
resolver's allow_default fallback is never actually exercised in practice), cg puzzle
import relies on this fallback for its everyday no-argument usage, and dropping
puzzle.json/data/ directly into whatever the current directory happens to be would be
a real footgun--confirmed live (2026-07-30): an earlier version of this fell back to bare
cwd and did exactly that. submit()-style callers, where there must already be a working
directory, should leave allow_default False.
Raises:
-
CgPuzzleDirNotFoundError–if no directory could be located anywhere, and
allow_defaultis False.
Source code in codingame_tools/puzzle_manager/resolver.py
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | |
parse_statement_html
¶
parse_statement_html(html)
Parse a puzzle's cached HTML statement into an ordered list of display blocks--see
CgStatementBlock and the module docstring.
Source code in codingame_tools/puzzle_manager/statement_render.py
169 170 171 172 173 174 175 | |