Skip to content

Commit 09823d2

Browse files
committed
Refine ladder win conditions, block binary pushes, quiet console logs
1 parent 933db22 commit 09823d2

6 files changed

Lines changed: 30 additions & 24 deletions

File tree

codeclash/agents/player.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,13 @@ def _get_commit_hash(self) -> str:
196196
def _commit(self) -> None:
197197
"""Commit changes to the agent's codebase."""
198198
r = self.game_context.round
199+
unstage_binaries = (
200+
"git diff --cached --numstat | awk -F'\\t' '$1==\"-\" && $2==\"-\" {print $3}' "
201+
"| xargs -r -d '\\n' git reset -q HEAD --"
202+
)
199203
for cmd in [
200204
"git add -A",
205+
unstage_binaries,
201206
f"git commit --allow-empty -m 'Round {r} Update'",
202207
]:
203208
assert_zero_exit_code(self.environment.execute(cmd), logger=self.logger)

codeclash/cli/ladder.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
def _resolve_ladder_rules(ladder_rules: dict, rounds: int) -> tuple[float, int]:
2222
"""Validate the optional ``ladder_rules`` block and return ``(min_round_win_fraction, win_last_k)``.
2323
24-
Defaults reproduce the historical behavior: win a strict majority of rounds
25-
(``min_round_win_fraction=0.5``) AND win the final round (``win_last_k=1``).
24+
Defaults: win at least ``min_round_win_fraction`` (0.4) of the *agent* rounds AND win the last
25+
``win_last_k`` (1) round(s). The baseline round 0 (identical, un-edited codebases) is excluded
26+
from this count — it reflects game variance, not the agent — so the fraction is taken over the
27+
``rounds`` rounds the agent actually edits.
2628
"""
27-
min_round_win_fraction = ladder_rules.get("min_round_win_fraction", 0.5)
29+
min_round_win_fraction = ladder_rules.get("min_round_win_fraction", 0.4)
2830
win_last_k = ladder_rules.get("win_last_k", 1)
2931

3032
# win_last_k: number of trailing rounds the player must win (1 == just the final round).
@@ -40,14 +42,15 @@ def _resolve_ladder_rules(ladder_rules: dict, rounds: int) -> tuple[float, int]:
4042
typer.echo(f"ladder_rules.win_last_k ({win_last_k}) cannot exceed tournament.rounds ({rounds}).")
4143
raise typer.Exit(1)
4244

43-
# min_round_win_fraction: player must win strictly more than this fraction of rounds.
45+
# min_round_win_fraction: player must win >= this fraction of the agent rounds (round 0 excluded).
4446
if isinstance(min_round_win_fraction, bool) or not isinstance(min_round_win_fraction, (int, float)):
4547
typer.echo(f"ladder_rules.min_round_win_fraction must be a number, got {min_round_win_fraction!r}.")
4648
raise typer.Exit(1)
47-
if not 0 <= min_round_win_fraction < 1:
49+
if not 0 <= min_round_win_fraction <= 1:
4850
typer.echo(
49-
f"ladder_rules.min_round_win_fraction must be in [0, 1), got {min_round_win_fraction}. "
50-
"0.5 requires a strict majority; 0 drops the majority requirement."
51+
f"ladder_rules.min_round_win_fraction must be in [0, 1], got {min_round_win_fraction}. "
52+
"The player must win >= this fraction of the agent rounds; 1 requires winning all of them, "
53+
"0 drops the fraction requirement."
5154
)
5255
raise typer.Exit(1)
5356

@@ -145,8 +148,8 @@ def run(
145148
config.pop("ladder_rules", None)
146149

147150
print(
148-
f"Ladder advancement rule: win > {min_round_win_fraction:.0%} of {rounds} rounds "
149-
f"and win the last {win_last_k} round(s)."
151+
f"Ladder advancement rule: win >= {min_round_win_fraction:.0%} of {rounds} agent rounds "
152+
f"(baseline round 0 excluded) and win the last {win_last_k} round(s)."
150153
)
151154
ladder_folder = f"LadderTournament.{config['game']['name']}.r{rounds}.s{sims}.{timestamp}"
152155
player["branch"] = ladder_folder
@@ -185,21 +188,21 @@ def run(
185188
metadata_path = tournament_dir / "metadata.json"
186189
with open(metadata_path) as f:
187190
metadata = yaml.safe_load(f)
188-
round_winners = [r["winner"] for r in metadata["round_stats"].values()]
191+
round_winners = [r["winner"] for k, r in metadata["round_stats"].items() if int(k) != 0]
189192

190-
# Advancement rule (configurable via `ladder_rules`): win strictly more than
191-
# `min_round_win_fraction` of rounds AND win the last `win_last_k` rounds.
193+
# Advancement rule (configurable via `ladder_rules`): win at least
194+
# `min_round_win_fraction` of the agent rounds AND win the last `win_last_k` rounds.
192195
player_wins = sum(1 for w in round_winners if w == player["name"])
193-
won_majority = player_wins > len(round_winners) * min_round_win_fraction
196+
won_majority = player_wins >= len(round_winners) * min_round_win_fraction
194197
won_last_k = all(w == player["name"] for w in round_winners[-win_last_k:])
195198

196199
if not won_majority or not won_last_k:
197200
# Player failed the advancement rule; the ladder challenge ends here.
198201
print("=" * 10)
199202
print(
200203
f"{player['name']} did not clear {opponent['name']} "
201-
f"(rank {opponent_rank}/{len(ladder)}): won {player_wins}/{len(round_winners)} rounds "
202-
f"(needed > {min_round_win_fraction:.0%}), last {win_last_k} round(s) won: {won_last_k}.\n"
204+
f"(rank {opponent_rank}/{len(ladder)}): won {player_wins}/{len(round_winners)} agent rounds "
205+
f"(needed >= {min_round_win_fraction:.0%}), last {win_last_k} round(s) won: {won_last_k}.\n"
203206
"Ladder challenge ends."
204207
)
205208
print("=" * 10)

codeclash/utils/log.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from __future__ import annotations
22

33
import logging
4+
import os
45
from pathlib import Path
56

67
from rich.console import Console
78
from rich.text import Text
89

9-
_STREAM_LEVEL = logging.DEBUG
10+
_STREAM_LEVEL = getattr(logging, os.getenv("CODECLASH_LOG_LEVEL", "INFO").upper(), logging.INFO)
1011
_FILE_LEVEL = logging.DEBUG
1112

1213
# logging.getLogger().setLevel(logging.DEBUG)

codeclash/utils/yaml_utils.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ def include_replacer(match):
2323
if prefix.strip() == "<<:":
2424
# For merge keys, include the content with proper indentation
2525
if indentation:
26-
# Merged content must be indented deeper than the `<<:` key itself, otherwise
27-
# the key gets a null scalar and PyYAML rejects the merge. (+2 spaces, matching
28-
# the no-indentation branch below.)
2926
content_indent = indentation + " "
3027
included_lines = included_content.splitlines()
3128
indented_lines = [content_indent + line if line.strip() else line for line in included_lines]

configs/ablations/ladder/battlesnake_llama_smoke.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# Cheap smoke to validate the llama-endpoint wiring before a full ladder run:
22
# Opus 4.8 (native Anthropic API via AnthropicModel) climbs a 2-rung BattleSnake ladder.
3-
# 1 round, 10 sims — just enough to confirm the model authenticates, caches, and edits code.
3+
# 2 rounds, 10 sims — just enough to confirm the model authenticates, caches, and edits code.
44
# Requires LLAMA_API_KEY in .env.
55
# uv run codeclash ladder run configs/ablations/ladder/battlesnake_llama_smoke.yaml -c
66
tournament:
7-
rounds: 1
7+
rounds: 2
88
ladder_rules:
9-
min_round_win_fraction: 0.5 # advance only on a strict majority of rounds
10-
win_last_k: 1 # ...and win the last K rounds (K=1 == just the final round)
9+
min_round_win_fraction: 0.5 # win >= half the agent rounds (round 0 excluded)
10+
win_last_k: 1 # ...and win the last K rounds (K=1 == the final round)
1111
game:
1212
name: BattleSnake
1313
sims_per_round: 10

configs/mini/default.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,4 @@ instance_template: |
101101
run exactly `echo COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT` to submit your work.
102102
You cannot continue working in any way on this task after submitting.
103103
step_limit: 30
104-
cost_limit: 0 # 0 disables the cost cap (see agents/default.py: limit applies only when >0); step_limit is the sole stop
104+
cost_limit: 0

0 commit comments

Comments
 (0)