JustinTX commited on
Commit
f4790e1
·
verified ·
1 Parent(s): 1255e4d

Add files using upload-large-folder tool

Browse files
Files changed (50) hide show
  1. benchmarks/ADRS/README.md +63 -0
  2. benchmarks/ADRS/eplb/evaluator/requirements.txt +1 -0
  3. benchmarks/ADRS/llm_sql/evaluator/download_dataset.sh +30 -0
  4. benchmarks/ADRS/llm_sql/evaluator/requirements.txt +2 -0
  5. benchmarks/ADRS/llm_sql/initial_program.py +365 -0
  6. benchmarks/ale_bench/ale-bench-lite-problems/ahc008/config.yaml +116 -0
  7. benchmarks/ale_bench/ale-bench-lite-problems/ahc008/initial_program.cpp +508 -0
  8. benchmarks/ale_bench/ale-bench-lite-problems/ahc011/best_program.cpp +730 -0
  9. benchmarks/ale_bench/ale-bench-lite-problems/ahc011/config.yaml +208 -0
  10. benchmarks/ale_bench/ale-bench-lite-problems/ahc011/evaluator.py +65 -0
  11. benchmarks/ale_bench/ale-bench-lite-problems/ahc011/initial_program.cpp +607 -0
  12. benchmarks/ale_bench/ale-bench-lite-problems/ahc015/best_program.cpp +664 -0
  13. benchmarks/ale_bench/ale-bench-lite-problems/ahc015/config.yaml +77 -0
  14. benchmarks/ale_bench/ale-bench-lite-problems/ahc015/initial_program.cpp +491 -0
  15. benchmarks/ale_bench/ale-bench-lite-problems/ahc016/best_program.cpp +244 -0
  16. benchmarks/ale_bench/ale-bench-lite-problems/ahc016/config.yaml +108 -0
  17. benchmarks/ale_bench/ale-bench-lite-problems/ahc016/evaluator.py +65 -0
  18. benchmarks/ale_bench/ale-bench-lite-problems/ahc016/initial_program.cpp +495 -0
  19. benchmarks/ale_bench/ale-bench-lite-problems/ahc024/best_program.cpp +626 -0
  20. benchmarks/ale_bench/ale-bench-lite-problems/ahc024/config.yaml +73 -0
  21. benchmarks/ale_bench/ale-bench-lite-problems/ahc024/evaluator.py +65 -0
  22. benchmarks/ale_bench/ale-bench-lite-problems/ahc024/initial_program.cpp +481 -0
  23. benchmarks/ale_bench/ale-bench-lite-problems/ahc025/best_program.cpp +282 -0
  24. benchmarks/ale_bench/ale-bench-lite-problems/ahc025/config.yaml +104 -0
  25. benchmarks/ale_bench/ale-bench-lite-problems/ahc025/evaluator.py +65 -0
  26. benchmarks/ale_bench/ale-bench-lite-problems/ahc025/initial_program.cpp +628 -0
  27. benchmarks/ale_bench/ale-bench-lite-problems/ahc026/best_program.cpp +653 -0
  28. benchmarks/ale_bench/ale-bench-lite-problems/ahc026/config.yaml +69 -0
  29. benchmarks/ale_bench/ale-bench-lite-problems/ahc026/evaluator.py +65 -0
  30. benchmarks/ale_bench/ale-bench-lite-problems/ahc026/initial_program.cpp +563 -0
  31. benchmarks/ale_bench/ale-bench-lite-problems/ahc027/best_program.cpp +595 -0
  32. benchmarks/ale_bench/ale-bench-lite-problems/ahc027/config.yaml +117 -0
  33. benchmarks/ale_bench/ale-bench-lite-problems/ahc027/evaluator.py +65 -0
  34. benchmarks/ale_bench/ale-bench-lite-problems/ahc027/initial_program.cpp +614 -0
  35. benchmarks/ale_bench/ale-bench-lite-problems/ahc039/config.yaml +77 -0
  36. benchmarks/ale_bench/ale-bench-lite-problems/ahc039/evaluator.py +65 -0
  37. benchmarks/ale_bench/ale-bench-lite-problems/ahc046/config.yaml +62 -0
  38. benchmarks/ale_bench/ale-bench-lite-problems/ahc046/evaluator.py +65 -0
  39. benchmarks/ale_bench/ale-bench-lite-problems/ahc046/initial_program.cpp +897 -0
  40. benchmarks/ale_bench/ale_agent_best/ahc016.cpp +495 -0
  41. benchmarks/ale_bench/private_eval.py +161 -0
  42. benchmarks/arc_benchmark/README.md +108 -0
  43. configs/README.md +355 -0
  44. configs/adaevolve.yaml +125 -0
  45. configs/default.yaml +38 -0
  46. configs/evox.yaml +59 -0
  47. configs/human_in_the_loop.yaml +49 -0
  48. configs/llm_judge.yaml +40 -0
  49. configs/openevolve_native.yaml +70 -0
  50. pyproject.toml +124 -0
benchmarks/ADRS/README.md ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ADRS: AI-Driven Research for Systems
2
+
3
+ This directory contains the systems optimization benchmarks from the **AI-Driven Research for Systems (ADRS)** initiative at UC Berkeley.
4
+
5
+ ADRS investigates how AI — large language models, evolutionary algorithms, and multi-agent architectures — can autonomously design, optimize, and evaluate computer systems. Instead of treating systems research as a purely manual process, ADRS frames it as a closed-loop optimization problem: propose candidate algorithms, evaluate them against system-level objectives, analyze failure modes, adapt the search strategy, and iterate.
6
+
7
+ Each benchmark below defines a concrete systems task with a provided evaluator, initial program, and configuration. Solutions are evolved using SkyDiscover's evolutionary search loop.
8
+
9
+ ## Benchmarks
10
+
11
+ ### Cloudcast — Multi-Cloud Data Transfer
12
+
13
+ **Directory:** `cloudcast/`
14
+
15
+ Given a network of cloud regions with heterogeneous egress pricing and bandwidth, broadcast a dataset from a source region to multiple destinations at minimum total cost. The evolved algorithm must construct routing topologies (e.g., relay trees, Steiner-like structures) that exploit shared intermediate hops across transfers.
16
+
17
+ ### Expert Parallelism Load Balancer (EPLB)
18
+
19
+ **Directory:** `eplb/`
20
+
21
+ In Mixture-of-Experts (MoE) model inference, a small subset of experts handles each token, leading to GPU load imbalance when certain experts become disproportionately popular. This task evolves an algorithm that decides how many replicas each expert should have and how to assign them across GPUs, optimizing both load-balance quality and rebalancing runtime.
22
+
23
+ ### Model Placement (Prism)
24
+
25
+ **Directory:** `prism/`
26
+
27
+ Assign multiple LLM models to a fixed GPU cluster (80 GB per GPU) such that the worst-case KV-cache pressure ratio across GPUs is minimized. Lower pressure means more memory headroom for serving, improving throughput and stability under varying request loads.
28
+
29
+ ### LLM-SQL — Column Reordering for Prefix Caching
30
+
31
+ **Directory:** `llm_sql/`
32
+
33
+ When rows of a table are serialized into LLM prompts sequentially, consecutive rows that share leading column values can reuse cached prefixes. This task evolves a column-reordering strategy that maximizes prefix-cache hit rates across multiple real-world datasets without altering the underlying data.
34
+
35
+ ### Transaction Scheduling (TXN)
36
+
37
+ **Directory:** `txn_scheduling/`
38
+
39
+ Given a set of database transactions with read/write dependencies on shared keys, find an execution ordering that minimizes the total makespan. The evolved scheduler must respect conflict constraints (read-write and write-write on the same key) while compressing the overall completion time.
40
+
41
+ ### Telemetry Repair
42
+
43
+ **Coming soon.** The Telemetry Repair benchmark is under active development and will be released in a future update.
44
+
45
+ ## Quick Start
46
+
47
+ Each benchmark directory contains:
48
+ - `initial_program.py` — the seed solution for evolution
49
+ - `evaluator.py` — the scoring function
50
+ - `config.yaml` — run configuration
51
+
52
+ Run any benchmark from the repo root:
53
+
54
+ ```bash
55
+ uv run skydiscover-run \
56
+ benchmarks/ADRS/cloudcast/initial_program.py \
57
+ benchmarks/ADRS/cloudcast/evaluator.py \
58
+ -c benchmarks/ADRS/cloudcast/config.yaml \
59
+ -s [your_algorithm] \
60
+ -i 100
61
+ ```
62
+
63
+ See the individual benchmark directories for task-specific setup instructions (e.g., dataset downloads, GPU dependencies).
benchmarks/ADRS/eplb/evaluator/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ torch
benchmarks/ADRS/llm_sql/evaluator/download_dataset.sh ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Download CSV datasets for the LLM-SQL benchmark.
3
+ #
4
+ # Required files (placed in datasets/):
5
+ # movies.csv - Rotten Tomatoes movie reviews (~9 MB)
6
+ # beer.csv - Beer review dataset (~2.5 MB)
7
+ # BIRD.csv - BIRD text-to-SQL dataset (~34 MB)
8
+ # PDMX.csv - PDMX metadata dataset (~7.4 MB)
9
+ # products.csv - Amazon product catalog (~16 MB)
10
+ #
11
+ # Usage:
12
+ # cd benchmarks/ADRS/llm_sql
13
+ # bash download_dataset.sh
14
+
15
+ set -euo pipefail
16
+ cd "$(dirname "$0")"
17
+
18
+ BASE_URL="https://huggingface.co/datasets/f20180301/adrs-data/resolve/main/llm_sql"
19
+
20
+ echo "Downloading LLM-SQL benchmark datasets..."
21
+
22
+ mkdir -p datasets
23
+ for dataset in movies.csv beer.csv BIRD.csv PDMX.csv products.csv; do
24
+ echo " Downloading datasets/${dataset}..."
25
+ wget -q --show-progress -O "datasets/${dataset}" "${BASE_URL}/datasets/${dataset}"
26
+ done
27
+
28
+ echo ""
29
+ echo "Done. Downloaded files:"
30
+ ls -lh datasets/*.csv
benchmarks/ADRS/llm_sql/evaluator/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ pandas
2
+ networkx>=3.2,<3.4
benchmarks/ADRS/llm_sql/initial_program.py ADDED
@@ -0,0 +1,365 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import pandas as pd
3
+ from solver import Algorithm
4
+ from typing import Tuple, List, Dict
5
+ from concurrent.futures import ThreadPoolExecutor, as_completed
6
+ from functools import lru_cache
7
+ from collections import Counter
8
+ import networkx as nx
9
+
10
+
11
+ class Evolved(Algorithm):
12
+ """
13
+ GGR algorithm
14
+ """
15
+
16
+ def __init__(self, df: pd.DataFrame = None):
17
+ self.df = df
18
+
19
+ self.dep_graph = None # NOTE: not used, for one way dependency
20
+
21
+ self.num_rows = 0
22
+ self.num_cols = 0
23
+ self.column_stats = None
24
+ self.val_len = None
25
+ self.row_stop = None
26
+ self.col_stop = None
27
+ self.base = 2000
28
+
29
+ def find_max_group_value(self, df: pd.DataFrame, value_counts: Dict, early_stop: int = 0) -> str:
30
+ # NOTE: recalculate value counts and length for each value
31
+ value_counts = Counter(df.stack())
32
+ weighted_counts = {val: self.val_len[val] * (count - 1) for val, count in value_counts.items()} # if count > 1
33
+ if not weighted_counts:
34
+ return None
35
+ max_group_val, max_weighted_count = max(weighted_counts.items(), key=lambda x: x[1])
36
+ if max_weighted_count < early_stop:
37
+ return None
38
+ return max_group_val
39
+
40
+ def reorder_columns_for_value(self, row, value, column_names, grouped_rows_len: int = 1):
41
+ # cols_with_value will now use attribute access instead of indexing with row[]
42
+ cols_with_value = []
43
+ for idx, col in enumerate(column_names):
44
+ if hasattr(row, col) and getattr(row, col) == value:
45
+ cols_with_value.append(col)
46
+ elif hasattr(row, col.replace(" ", "_")) and getattr(row, col.replace(" ", "_")) == value:
47
+ cols_with_value.append(col)
48
+ else:
49
+ attr_name = f"_{idx}"
50
+ if hasattr(row, attr_name) and getattr(row, attr_name) == value:
51
+ cols_with_value.append(attr_name)
52
+
53
+ if self.dep_graph is not None and grouped_rows_len > 1:
54
+ # NOTE: experimental
55
+ reordered_cols = []
56
+ for col in cols_with_value:
57
+ dependent_cols = self.get_dependent_columns(col)
58
+
59
+ # check if dependent columns are in row, and if column exists in row attributes
60
+ valid_dependent_cols = []
61
+ for idx, dep_col in enumerate(dependent_cols):
62
+ if hasattr(row, dep_col):
63
+ valid_dependent_cols.append(dep_col)
64
+ elif hasattr(row, dep_col.replace(" ", "_")):
65
+ valid_dependent_cols.append(dep_col)
66
+ else:
67
+ attr_name = f"_{idx}"
68
+ if hasattr(row, attr_name):
69
+ valid_dependent_cols.append(dep_col)
70
+
71
+ reordered_cols.extend([col] + valid_dependent_cols)
72
+ cols_without_value = [col for col in column_names if col not in reordered_cols]
73
+ reordered_cols.extend(cols_without_value)
74
+ assert len(reordered_cols) == len(
75
+ column_names
76
+ ), f"Reordered cols len: {len(reordered_cols)} Original cols len: {len(column_names)}"
77
+ return [getattr(row, col) for col in reordered_cols], cols_with_value
78
+ else:
79
+ cols_without_value = []
80
+ for idx, col in enumerate(column_names):
81
+ if hasattr(row, col) and getattr(row, col) != value:
82
+ cols_without_value.append(col)
83
+ elif hasattr(row, col.replace(" ", "_")) and getattr(row, col.replace(" ", "_")) != value:
84
+ cols_without_value.append(col)
85
+ else:
86
+ # Handle some edge cases
87
+ attr_name = f"_{idx}"
88
+ if hasattr(row, attr_name) and getattr(row, attr_name) != value:
89
+ cols_without_value.append(attr_name)
90
+
91
+ reordered_cols = cols_with_value + cols_without_value
92
+ assert len(reordered_cols) == len(
93
+ column_names
94
+ ), f"Reordered cols len: {len(reordered_cols)} Original cols len: {len(column_names)}"
95
+ return [getattr(row, col) for col in reordered_cols], cols_with_value
96
+
97
+ def get_dependent_columns(self, col: str) -> List[str]:
98
+ if self.dep_graph is None or not self.dep_graph.has_node(col):
99
+ return []
100
+ return list(nx.descendants(self.dep_graph, col))
101
+
102
+ @lru_cache(maxsize=None)
103
+ def get_cached_dependent_columns(self, col: str) -> List[str]:
104
+ return self.get_dependent_columns(col)
105
+
106
+ def fixed_reorder(self, df: pd.DataFrame, row_sort: bool = True) -> Tuple[pd.DataFrame, List[List[str]]]:
107
+ num_rows, column_stats = self.calculate_col_stats(df, enable_index=True)
108
+ reordered_columns = [col for col, _, _, _ in column_stats]
109
+ reordered_df = df[reordered_columns]
110
+
111
+ assert reordered_df.shape == df.shape
112
+ column_orderings = [reordered_columns] * num_rows
113
+
114
+ if row_sort:
115
+ reordered_df = reordered_df.sort_values(by=reordered_columns, axis=0)
116
+
117
+ return reordered_df, column_orderings
118
+
119
+ def column_recursion(self, result_df, max_value, grouped_rows, row_stop, col_stop, early_stop):
120
+ cols_settled = []
121
+ with ThreadPoolExecutor() as executor:
122
+ futures = [
123
+ executor.submit(self.reorder_columns_for_value, row, max_value, grouped_rows.columns.tolist(), len(grouped_rows))
124
+ for row in grouped_rows.itertuples(index=False)
125
+ ]
126
+ for i, future in enumerate(as_completed(futures)):
127
+ reordered_row, cols_settled = future.result()
128
+ result_df.loc[i] = reordered_row
129
+
130
+ grouped_value_counts = Counter()
131
+
132
+ if not result_df.empty:
133
+ # Group by the first column
134
+ grouped_result_df = result_df.groupby(result_df.columns[0])
135
+ grouped_value_counts = Counter(grouped_rows.stack()) # this is still faster than updating from cached value counts
136
+
137
+ for _, group in grouped_result_df:
138
+ if group[group.columns[0]].iloc[0] != max_value:
139
+ continue
140
+
141
+ dependent_cols = self.get_cached_dependent_columns(group.columns[0])
142
+ length_of_settle_cols = len(cols_settled)
143
+
144
+ if dependent_cols:
145
+ assert length_of_settle_cols >= 1, f"Dependent columns should be no less than 1, but got {length_of_settle_cols}"
146
+
147
+ # test the first length_of_settle_cols columns, each column has nunique == 1
148
+ for col in group.columns[:length_of_settle_cols]:
149
+ assert group[col].nunique() == 1, f"Column {col} should have nunique == 1, but got {group[col].nunique()}"
150
+
151
+ # drop all the settled columns and reorder the rest
152
+ group_remainder = group.iloc[:, length_of_settle_cols:]
153
+ else:
154
+ group_remainder = group.iloc[:, 1:]
155
+
156
+ grouped_remainder_value_counts = Counter(group_remainder.stack())
157
+
158
+ reordered_group_remainder, _ = self.recursive_reorder(
159
+ group_remainder, grouped_remainder_value_counts, early_stop=early_stop, row_stop=row_stop, col_stop=col_stop + 1
160
+ )
161
+ # Update the group with the reordered columns
162
+ if dependent_cols:
163
+ group.iloc[:, length_of_settle_cols:] = reordered_group_remainder.values
164
+ else:
165
+ group.iloc[:, 1:] = reordered_group_remainder.values
166
+
167
+ result_df.update(group)
168
+ break
169
+
170
+ return result_df, grouped_value_counts
171
+
172
+ def recursive_reorder(
173
+ self,
174
+ df: pd.DataFrame,
175
+ value_counts: Dict,
176
+ early_stop: int = 0,
177
+ original_columns: List[str] = None,
178
+ row_stop: int = 0,
179
+ col_stop: int = 0,
180
+ ) -> Tuple[pd.DataFrame, List[List[str]]]:
181
+ if df.empty or len(df.columns) == 0 or len(df) == 0:
182
+ return df, []
183
+
184
+ if self.row_stop is not None and row_stop >= self.row_stop:
185
+ return self.fixed_reorder(df)
186
+
187
+ if self.col_stop is not None and col_stop >= self.col_stop:
188
+ return self.fixed_reorder(df)
189
+
190
+ if original_columns is None:
191
+ original_columns = df.columns.tolist()
192
+
193
+ # Find the max group value using updated counts
194
+ max_value = self.find_max_group_value(df, value_counts, early_stop=early_stop)
195
+ if max_value is None:
196
+ # If there is no max value, then fall back to fixed reorder
197
+ return self.fixed_reorder(df)
198
+
199
+ grouped_rows = df[df.isin([max_value]).any(axis=1)]
200
+ remaining_rows = df[~df.isin([max_value]).any(axis=1)]
201
+
202
+ # If there is no grouped rows, return the original DataFrame
203
+ if grouped_rows.empty:
204
+ return self.fixed_reorder(df)
205
+
206
+ result_df = pd.DataFrame(columns=df.columns)
207
+
208
+ reordered_remaining_rows = pd.DataFrame(columns=df.columns) # Initialize empty dataframe first
209
+
210
+ # Column Recursion
211
+ result_df, grouped_value_counts = self.column_recursion(result_df, max_value, grouped_rows, row_stop, col_stop, early_stop)
212
+
213
+ remaining_value_counts = value_counts - grouped_value_counts # Approach 1 - update remaining value counts with subtraction
214
+
215
+ # Row Recursion
216
+ reordered_remaining_rows, _ = self.recursive_reorder(
217
+ remaining_rows, remaining_value_counts, early_stop=early_stop, row_stop=row_stop + 1, col_stop=col_stop
218
+ )
219
+ old_column_names = result_df.columns.tolist()
220
+ result_cols_reset = result_df.reset_index(drop=True)
221
+ result_rows_reset = reordered_remaining_rows.reset_index(drop=True)
222
+ final_result_df = pd.DataFrame(result_cols_reset.values.tolist() + result_rows_reset.values.tolist())
223
+
224
+ if row_stop == 0 and col_stop == 0:
225
+ final_result_df.columns = old_column_names
226
+ final_result_df.columns = final_result_df.columns.tolist()[:-1] + ["original_index"]
227
+
228
+ return final_result_df, []
229
+
230
+ def recursive_split_and_reorder(self, df: pd.DataFrame, original_columns: List[str] = None, early_stop: int = 0):
231
+ """
232
+ Recursively split the DataFrame into halves until the size is <= 1000, then apply the recursive reorder function.
233
+ """
234
+ if len(df) <= self.base:
235
+ initial_value_counts = Counter(df.stack())
236
+ return self.recursive_reorder(df, initial_value_counts, early_stop, original_columns, row_stop=0, col_stop=0)[0]
237
+
238
+ mid_index = len(df) // 2
239
+ df_top_half = df.iloc[:mid_index]
240
+ df_bottom_half = df.iloc[mid_index:]
241
+
242
+ with ThreadPoolExecutor() as executor:
243
+ future_top = executor.submit(self.recursive_split_and_reorder, df_top_half, original_columns, early_stop)
244
+ future_bottom = executor.submit(self.recursive_split_and_reorder, df_bottom_half, original_columns, early_stop)
245
+
246
+ reordered_top_half = future_top.result()
247
+ reordered_bottom_half = future_bottom.result()
248
+
249
+ assert reordered_bottom_half.shape == df_bottom_half.shape
250
+ reordered_df = pd.concat([reordered_top_half, reordered_bottom_half], axis=0, ignore_index=True)
251
+
252
+ assert reordered_df.shape == df.shape
253
+
254
+ return reordered_df
255
+
256
+ @lru_cache(maxsize=None)
257
+ def calculate_length(self, value):
258
+ if isinstance(value, bool):
259
+ return 4**2
260
+ if isinstance(value, (int, float)):
261
+ return len(str(value)) ** 2
262
+ if isinstance(value, str):
263
+ return len(value) ** 2
264
+ return 0
265
+
266
+ def reorder(
267
+ self,
268
+ df: pd.DataFrame,
269
+ early_stop: int = 0,
270
+ row_stop: int = None,
271
+ col_stop: int = None,
272
+ col_merge: List[List[str]] = [],
273
+ one_way_dep: List[Tuple[str, str]] = [],
274
+ distinct_value_threshold: float = 0.8,
275
+ parallel: bool = True,
276
+ ) -> Tuple[pd.DataFrame, List[List[str]]]:
277
+ # Prepare
278
+ initial_df = df.copy()
279
+ if col_merge:
280
+ self.num_rows, self.column_stats = self.calculate_col_stats(df, enable_index=True)
281
+ reordered_columns = [col for col, _, _, _ in self.column_stats]
282
+ for col_to_merge in col_merge:
283
+ final_col_order = [col for col in reordered_columns if col in col_to_merge]
284
+ df = self.merging_columns(df, final_col_order, prepended=False)
285
+ self.num_rows, self.column_stats = self.calculate_col_stats(df, enable_index=True)
286
+ self.column_stats = {col: (num_groups, avg_len, score) for col, num_groups, avg_len, score in self.column_stats}
287
+
288
+ # One way dependency statistics [not used]
289
+ if one_way_dep is not None and len(one_way_dep) > 0:
290
+ self.dep_graph = nx.DiGraph()
291
+ for dep in one_way_dep:
292
+ col1 = [col for col in df.columns if dep[0] in col]
293
+ col2 = [col for col in df.columns if dep[1] in col]
294
+ assert len(col1) == 1, f"Expected one column to match {dep[0]}, but got {len(col1)}"
295
+ assert len(col2) == 1, f"Expected one column to match {dep[1]}, but got {len(col2)}"
296
+ col1 = col1[0]
297
+ col2 = col2[0]
298
+ self.dep_graph.add_edge(col1, col2)
299
+
300
+ # Discard too distinct columns by threshold [optional]
301
+ nunique_threshold = len(df) * distinct_value_threshold
302
+ columns_to_discard = [col for col in df.columns if df[col].nunique() > nunique_threshold]
303
+ columns_to_discard = sorted(columns_to_discard, key=lambda x: self.column_stats[x][2], reverse=True)
304
+ columns_to_recurse = [col for col in df.columns if col not in columns_to_discard]
305
+ df["original_index"] = range(len(df))
306
+ discarded_columns_df = df[columns_to_discard + ["original_index"]]
307
+ df_to_recurse = df[columns_to_recurse + ["original_index"]]
308
+ recurse_df = df_to_recurse
309
+
310
+ self.column_stats = {col: stats for col, stats in self.column_stats.items() if col not in columns_to_discard}
311
+ initial_value_counts = Counter(recurse_df.stack())
312
+ self.val_len = {val: self.calculate_length(val) for val in initial_value_counts.keys()}
313
+
314
+ self.row_stop = row_stop if row_stop else len(recurse_df)
315
+ self.col_stop = col_stop if col_stop else len(recurse_df.columns.tolist())
316
+ print("*" * 80)
317
+ print(f"DF columns = {df.columns}")
318
+ # print(f"Early stop = {early_stop}")
319
+ # print(f"Row recursion stop depth = {self.row_stop}, Column recursion stop depth = {self.col_stop}")
320
+ print("*" * 80)
321
+
322
+ # Eary stop and fall back
323
+ recurse_df, _ = self.fixed_reorder(recurse_df)
324
+
325
+ # Recursive reordering
326
+ self.num_cols = len(recurse_df.columns)
327
+ if parallel:
328
+ reordered_df = self.recursive_split_and_reorder(recurse_df, original_columns=columns_to_recurse, early_stop=early_stop)
329
+ else:
330
+ reordered_df, _ = self.recursive_reorder(
331
+ recurse_df,
332
+ initial_value_counts,
333
+ early_stop=early_stop,
334
+ )
335
+
336
+ assert (
337
+ reordered_df.shape == recurse_df.shape
338
+ ), f"Reordered DataFrame shape {reordered_df.shape} does not match original DataFrame shape {recurse_df.shape}"
339
+ assert recurse_df["original_index"].is_unique, "Passed in recurse index contains duplicates!"
340
+ assert reordered_df["original_index"].is_unique, "Reordered index contains duplicates!"
341
+
342
+ if len(columns_to_discard) > 0:
343
+ final_df = pd.merge(reordered_df, discarded_columns_df, on="original_index", how="left")
344
+ else:
345
+ final_df = reordered_df
346
+
347
+ final_df = final_df.drop(columns=["original_index"])
348
+
349
+ if not col_merge:
350
+ assert (
351
+ final_df.shape == initial_df.shape
352
+ ), f"Final DataFrame shape {final_df.shape} does not match original DataFrame shape {initial_df.shape}"
353
+ else:
354
+ assert (
355
+ final_df.shape[0] == initial_df.shape[0]
356
+ ), f"Final DataFrame shape {final_df.shape} does not match original DataFrame shape {initial_df.shape}"
357
+ assert (
358
+ final_df.shape[1] == recurse_df.shape[1] + len(columns_to_discard) - 1
359
+ ), f"Final DataFrame shape {final_df.shape} does not match original DataFrame shape {recurse_df.shape}"
360
+
361
+ # sort by the first column to get the final order
362
+ final_df = final_df.sort_values(by=final_df.columns.to_list(), axis=0)
363
+ return final_df, []
364
+
365
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc008/config.yaml ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ALE-Bench ahc008 — AtCoder Heuristic Contest
2
+ # Usage: skydiscover-run initial_program.cpp evaluator.py -c config.yaml -s <strategy>
3
+ language: cpp
4
+ diff_based_generation: true
5
+ max_iterations: 100
6
+ checkpoint_interval: 10
7
+ max_solution_length: 60000
8
+ llm:
9
+ api_base: https://api.openai.com/v1
10
+ models:
11
+ - name: "gpt-5"
12
+ weight: 1.0
13
+ max_tokens: 32000
14
+ timeout: 600
15
+ prompt:
16
+ system_message: "You are a world-class algorithm engineer, and you are very good at programming. Now, you are participating\
17
+ \ in a programming contest. You are asked to solve a heuristic problem, known as an NP-hard problem.\n\nStory\n--------\n\
18
+ AtCoder's CEO, Takahashi, loves animals and has a number of pets running free in the AtCoder office.\nAtCoder's employees\
19
+ \ have trouble with the pets interrupting their work, so they have decided to place partitions in the office to create\
20
+ \ a space where pets cannot come in.\nPlease create as large a space as possible.\n\nProblem Statement\n--------\nThere\
21
+ \ are $N$ pets and $M$ people in a room with a floor of $30 \\times 30$ squares.\nAll squares are initially passable,\
22
+ \ and outside of the $30 \\times 30$ squares are impassable.\nLet $(x, y)$ be the coordinates of the square in row $x$\
23
+ \ from the top ($1\\leq x\\leq 30$) and column $y$ from the left ($1\\leq y\\leq 30$).\nRepeat the following process for\
24
+ \ $300$ turns.\n\nFirst, you choose each person's action from the following three types, and perform each action simultaneously.\n\
25
+ \n- Do nothing and stay in the current position.\n- Choose a square adjacent to the current position and make it impassable.\
26
+ \ You cannot choose a square that contains pets or humans at the start of this turn. <b>You cannot choose a square whose\
27
+ \ adjacent square contains a pet, either.</b> If you choose a square that is already impassable, nothing happens.\n- Move\
28
+ \ to an adjacent passable square. It is not possible to move to a square that becomes impassable by another person's action\
29
+ \ in this turn.\n\nAfter all the people have completed their actions for that turn, each pet moves independently.\nRules\
30
+ \ for pet movement depend on the type of pet, and some pets may move multiple squares in a single turn.\nDetails are described\
31
+ \ later.\n\nSquares containing humans or pets are also passable, and each square can contain any number of humans and\
32
+ \ pets.\n\n\nScoring\n--------\nAt the end of $300$ turn, for each $i=1,\\cdots,M$, let $R_i$ be the set of squares reachable\
33
+ \ from the final position of person $i$ through only passable squares, and $n_i$ be the number of pets whose final position\
34
+ \ is in $R_i$.\nThen, person $i$ obtains satisfaction of $s_i=\\frac{|R_i|}{900}\\times 2^{-n_i}$.\nThe score for the\
35
+ \ test case is $\\mathrm{round}\\left(10^8\\times\\frac{1}{M}\\sum_{i=1}^M s_i\\right)$.\n\n#### Number of test cases\n\
36
+ - Provisional test: 100\n- System test: 2000. We will publish <a href=\"https://img.atcoder.jp/ahc008/seeds.txt\">seeds.txt</a>\
37
+ \ (md5=27bf0702bbe0265900374c3b6b9846b4, sha256=33973e4ded08e3a607fc2e841e14751ff110ae10154b286e7fd5f766ff86d706) after\
38
+ \ the contest is over.\n\nThe score of a submission is the total scores for each test case.\nIn the provisional test,\
39
+ \ if your submission produces illegal output or exceeds the time limit for some test cases, the submission itself will\
40
+ \ be judged as <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Wrong Answer\">WA</span>\
41
+ \ or <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Time Limit Exceeded\">TLE</span>\
42
+ \ , and the score of the submission will be zero.\nIn the system test, if your submission produces illegal output or exceeds\
43
+ \ the time limit for some test cases, only the score for those test cases will be zero.\nNote that if your program terminates\
44
+ \ abnormally, it may be judged as <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"\
45
+ Wrong Answer\">WA</span> instead of <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"\
46
+ Runtime Error\">RE</span>.\n\n#### About execution time\nExecution time may vary slightly from run to run.\nIn addition,\
47
+ \ since system tests simultaneously perform a large number of executions, it has been observed that execution time increases\
48
+ \ by several percent compared to provisional tests.\nFor these reasons, submissions that are very close to the time limit\
49
+ \ may result in <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Time Limit Exceeded\"\
50
+ >TLE</span> in the system test.\nPlease measure the execution time in your program to terminate the process, or have enough\
51
+ \ margin in the execution time.\n\n\nInput and Output\n--------\nFirst, the initial position and type of each pet, and\
52
+ \ the initial position of each person are given from Standard Input in the following format\n~~~\n$N$\n$px_1$ $py_1$ $pt_1$\n\
53
+ $\\vdots$\n$px_N$ $py_N$ $pt_N$\n$M$\n$hx_1$ $hy_1$\n$\\vdots$\n$hx_M$ $hy_M$\n~~~\n$N$ is an integer between $10$ and\
54
+ \ $20$ representing the number of pets.\n$(px_i,py_i)$ represents the coordinates of the initial position of the $i$-th\
55
+ \ pet, and $pt_i$ is an integer between $1$ and $5$ representing the type of the $i$-th pet.\n$M$ is an integer between\
56
+ \ $5$ and $10$ representing the number of humans.\n$(hx_i,hy_i)$ represents the coordinates of the initial position of\
57
+ \ the $i$-th human.\nThe initial positions of all pets and humans are guaranteed to be distinct.\n\nAfter reading the\
58
+ \ above information, repeat the following process $300$ turns.\n\nFirst, output a string of length $M$ where the $i$-th\
59
+ \ character represents the action of the $i$th person as follows on a single line to Standard Output.\n<font color=\"\
60
+ red\">**After the output, you have to flush Standard Output.**</font> Otherwise, the submission might be judged as <span\
61
+ \ class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Time Limit Exceeded\">TLE</span> .\n\n\
62
+ - `.`: Do nothing and stay in the current position.\n- `u`, `d`, `l`, `r`: Let $(x,y)$ be the current position. Make the\
63
+ \ square $(x-1,y)$, $(x+1,y)$, $(x,y-1)$, or $(x,y+1)$ impassable, respectively.\n- `U`, `D`, `L`, `R`: Let $(x,y)$ be\
64
+ \ the current position. Move to the the square $(x-1,y)$, $(x+1,y)$, $(x,y-1)$, or $(x,y+1)$, respectively.\n\nAfter the\
65
+ \ output, $N$ strings are given to Standard Input in a single line, separated by spaces.\nThe $i$-th string represents\
66
+ \ movement of the $i$-th pet in that turn.\nIf the pet does not move, the string is `.`.\nIf it does move, the string\
67
+ \ is a sequence of characters `U`, `D`, `L`, and `R` representing the movement of one square up, down, left, and right,\
68
+ \ respectively.\n\n<a href=\"https://img.atcoder.jp/ahc008/f828b9475ffb41d54f05619db6ccbd4f.html?lang=en&show=example\"\
69
+ >Show example</a>\n\n\nPets Movement Rules\n--------\nWe define a basic move as follows: move to a square chosen at random\
70
+ \ among the adjacent passable squares. From the condition of the squares that can be made impassable, such squares always\
71
+ \ exist.\n\nEach pet $i$ performs the following moves depending on $pt_i$, an integer value between $1$ and $5$ representing\
72
+ \ its type.\n\n1. <img src=\"./images/cow.png\" width=\"30\" height=\"30\" style=\"background-color:silver;image-rendering:pixelated\"\
73
+ > Cow: Perform one basic move.\n2. <img src=\"./images/pig.png\" width=\"30\" height=\"30\" style=\"background-color:silver;image-rendering:pixelated\"\
74
+ > Pig: Perform two basic moves.\n3. <img src=\"./images/rabbit.png\" width=\"30\" height=\"30\" style=\"background-color:silver;image-rendering:pixelated\"\
75
+ > Rabbit: Perform three basic moves.\n4. <img src=\"./images/dog.png\" width=\"30\" height=\"30\" style=\"background-color:silver;image-rendering:pixelated\"\
76
+ > Dog: Move toward a target person as follows. The first turn starts with no target. If it has no target, the target person\
77
+ \ is in the current position, or there exists no path to the target person, then it selects one person uniformly at random\
78
+ \ among those reachable from the current position, excluding those in the current position. If there is no such person,\
79
+ \ reset to no target and perform one basic move. Otherwise, move to an adjacent passable square that shortens the shortest\
80
+ \ distance to the target person (if there are multiple such squares, choose one of them uniformly at random), and then\
81
+ \ perform one basic move. If it reaches the destination after the first or the second move, reset to no target.\n5. <img\
82
+ \ src=\"./images/cat.png\" width=\"30\" height=\"30\" style=\"background-color:silver;image-rendering:pixelated\"> Cat:\
83
+ \ Move toward a target square as follows. The first turn starts with no target. If it has no target or there exists no\
84
+ \ path to the target square, then it selects one square uniformly at random among those reachable from the current position,\
85
+ \ excluding the current position. If there exists no such square, do nothing. Otherwise, move to an adjacent passable\
86
+ \ square that shortens the shortest distance to the target square (if there are multiple such squares, choose one of them\
87
+ \ uniformly at random), and then perform one basic move. If it reaches the destination after the first or the second move,\
88
+ \ reset to no target.\n\n\nInput Generation\n--------\nLet $\\mathrm{rand}(L,U)$ be a function that generates a uniform\
89
+ \ random integer between $L$ and $U$, inclusive.\n\nWe generate the number of pets by $N=\\mathrm{rand}(10, 20)$.\nThe\
90
+ \ initial position of each pet is chosen uniformly at random from the coordinates that have not been chosen yet.\nWe generate\
91
+ \ the type of each pet by $pt_i=\\mathrm{rand}(1, 5)$.\n\nWe generate the number of humans by $M=\\mathrm{rand}(5, 10)$.\n\
92
+ The initial position of each human is chosen uniformly at random from the coordinates that have not been chosen yet.\n\
93
+ \n\nTools\n--------\n- <a href=\"https://img.atcoder.jp/ahc008/tools_v3.zip\">Local tester</a>: You need a compilation\
94
+ \ environment of <a href=\"https://www.rust-lang.org/\">Rust language</a>.\n - For those who are not familiar with the\
95
+ \ Rust language environment, we have prepared a pre-compiled binary for Windows. <a href=\"https://img.atcoder.jp/ahc008/tools_x86_64-pc-windows-gnu_v3.zip\"\
96
+ >tools_x86_64-pc-windows-gnu.zip</a>\n - <font color=\"red\">The first version contained a bug in the cat's movement,\
97
+ \ which has been fixed at 130 minutes after the contest started. Please re-download it.</font>\n - We have added more\
98
+ \ examples in README. If you don't know how to use the tools, please refer to README. Also, as stated in the rules, you\
99
+ \ are free to share information on how to run the provided tools.\n- <a href=\"https://img.atcoder.jp/ahc008/f828b9475ffb41d54f05619db6ccbd4f.html?lang=en\"\
100
+ >Web visualizer</a>: By pasting the output generated by the local tester into the Output field, you can display the animation\
101
+ \ of the execution result.\n\n<font color=\"red\">You are allowed to share output images (png or gif) of the provided\
102
+ \ visualizer for seed=0 on twitter during the contest.</font> You have to use the specified hashtag and public account.\
103
+ \ You can only share visualization results and scores for seed=0. Do not share scores for other seeds or mention solutions\
104
+ \ or discussions. <a href=\"https://twitter.com/search?q=%23AHC008%20%23visualizer&src=typed_query&f=live\">List of shared\
105
+ \ images.</a>\n\n#### Specification of input/output files used by the tools\nInput files for the local tester consist\
106
+ \ of the prior information (the initial position and type of each pet, and the initial position of each person) followed\
107
+ \ by a random seed value to generate pet movements.\nSince the pet's movement depends on human actions, the input file\
108
+ \ contains only the random seed value and not specific movements.\nThe local tester writes outputs from your program directly\
109
+ \ to the output file.\nYour program may output comment lines starting with `#`.\nThe web version of the visualizer displays\
110
+ \ the comment lines at the time they are output, which may be useful for debugging and analysis.\nSince the judge program\
111
+ \ ignores all comment lines, you can submit a program that outputs comment lines as is.\n\nProblem constraints:\ntime_limit=3.0\
112
+ \ memory_limit=1073741824\n"
113
+ evaluator:
114
+ timeout: 10000
115
+ cascade_evaluation: false
116
+
benchmarks/ale_bench/ale-bench-lite-problems/ahc008/initial_program.cpp ADDED
@@ -0,0 +1,508 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include <string>
5
+ #include <algorithm>
6
+ // #include <map>
7
+ // #include <set>
8
+ #include <queue>
9
+ #include <cmath>
10
+ #include <iomanip>
11
+ #include <limits>
12
+
13
+ // --- Constants ---
14
+ constexpr int GRID_SIZE = 30;
15
+ constexpr int NUM_TURNS = 300;
16
+ constexpr int INF = std::numeric_limits<int>::max();
17
+
18
+ struct Point {
19
+ int r, c;
20
+
21
+ bool operator==(const Point& other) const { return r == other.r && c == other.c; }
22
+ bool operator!=(const Point& other) const { return !(*this == other); }
23
+ bool operator<(const Point& other) const {
24
+ if (r != other.r) return r < other.r;
25
+ return c < other.c;
26
+ }
27
+ };
28
+ const Point INVALID_POINT = {-1, -1};
29
+
30
+
31
+ // Tunable parameters
32
+ constexpr int STAND_OUTSIDE_INNER_SAFE_PENALTY = 1000;
33
+ constexpr int ADJACENT_WALL_PRIORITY_BONUS = 0;
34
+ constexpr int NEAR_PET_PENALTY_POINTS_PER_PET = 0;
35
+ constexpr int NEAR_PET_RADIUS = 2;
36
+ constexpr int MAX_STUCK_TURNS = 10; // Slightly increased
37
+
38
+ // Directions: Up, Down, Left, Right (indices 0, 1, 2, 3)
39
+ const Point DIRS[4] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
40
+ const char DIR_CHARS_BUILD[4] = {'u', 'd', 'l', 'r'};
41
+ const char DIR_CHARS_MOVE[4] = {'U', 'D', 'L', 'R'};
42
+ const char PET_MOVE_CHARS[4] = {'U', 'D', 'L', 'R'};
43
+
44
+ struct PetInfo {
45
+ Point pos;
46
+ int type;
47
+ int id;
48
+ };
49
+
50
+ enum class HumanObjective {
51
+ BUILDING_WALLS,
52
+ GOING_TO_SAFE_SPOT,
53
+ STAYING_IN_SAFE_SPOT,
54
+ REPOSITIONING_STUCK
55
+ // FLEEING_PET_IN_PEN removed, simplified objective setting
56
+ };
57
+
58
+ struct HumanInfo {
59
+ Point pos;
60
+ int id;
61
+
62
+ int strip_r_start;
63
+ int strip_r_end;
64
+
65
+ Point inner_safe_ul;
66
+ Point inner_safe_br;
67
+ Point final_stand_pos;
68
+
69
+ std::vector<Point> assigned_wall_cells;
70
+ HumanObjective objective;
71
+ int turns_stuck_building = 0;
72
+ };
73
+
74
+ // --- Game Grid and State ---
75
+ bool is_impassable_grid_static[GRID_SIZE + 1][GRID_SIZE + 1];
76
+ std::vector<PetInfo> pets_global_state;
77
+ std::vector<HumanInfo> humans_global_state;
78
+ int N_pets_global, M_humans_global;
79
+
80
+ Point bfs_parent_grid[GRID_SIZE + 1][GRID_SIZE + 1];
81
+ bool bfs_visited_grid[GRID_SIZE + 1][GRID_SIZE + 1];
82
+
83
+
84
+ // --- Utility Functions ---
85
+ bool is_valid_coord(int val) {
86
+ return val >= 1 && val <= GRID_SIZE;
87
+ }
88
+
89
+ bool is_valid_point(Point p) {
90
+ return is_valid_coord(p.r) && is_valid_coord(p.c);
91
+ }
92
+
93
+ int manhattan_distance(Point p1, Point p2) {
94
+ if (!is_valid_point(p1) || !is_valid_point(p2)) return INF;
95
+ return std::abs(p1.r - p2.r) + std::abs(p1.c - p2.c);
96
+ }
97
+
98
+ int count_adjacent_walls_or_boundaries(Point p) {
99
+ int count = 0;
100
+ for (int i = 0; i < 4; ++i) {
101
+ Point neighbor = {p.r + DIRS[i].r, p.c + DIRS[i].c};
102
+ if (!is_valid_point(neighbor) || (is_valid_point(neighbor) && is_impassable_grid_static[neighbor.r][neighbor.c])) {
103
+ count++;
104
+ }
105
+ }
106
+ return count;
107
+ }
108
+
109
+ bool can_theoretically_build_at(Point wall_pos, int builder_human_id) {
110
+ if (!is_valid_point(wall_pos)) return false;
111
+ if (is_impassable_grid_static[wall_pos.r][wall_pos.c]) return false;
112
+
113
+ for (const auto& pet : pets_global_state) {
114
+ if (pet.pos == wall_pos) return false;
115
+ if (manhattan_distance(wall_pos, pet.pos) == 1) return false;
116
+ }
117
+
118
+ for (const auto& human : humans_global_state) {
119
+ if (human.id == builder_human_id) continue; // Builder themself can be adjacent
120
+ if (human.pos == wall_pos) return false; // Other human on the wall_pos
121
+ }
122
+ return true;
123
+ }
124
+
125
+ char get_bfs_move_char(Point start_pos, Point target_pos,
126
+ const std::vector<Point>& current_turn_tentative_walls) {
127
+ if (start_pos == target_pos) return '.';
128
+
129
+ std::queue<Point> q;
130
+ q.push(start_pos);
131
+
132
+ for(int r_bfs = 1; r_bfs <= GRID_SIZE; ++r_bfs) for(int c_bfs = 1; c_bfs <= GRID_SIZE; ++c_bfs) {
133
+ bfs_visited_grid[r_bfs][c_bfs] = false;
134
+ bfs_parent_grid[r_bfs][c_bfs] = INVALID_POINT;
135
+ }
136
+ if (!is_valid_point(start_pos)) return '.';
137
+ bfs_visited_grid[start_pos.r][start_pos.c] = true;
138
+
139
+ Point path_found_dest = INVALID_POINT;
140
+
141
+ while(!q.empty()){
142
+ Point curr = q.front();
143
+ q.pop();
144
+
145
+ for(int i_dir=0; i_dir < 4; ++i_dir){
146
+ Point next_p = {curr.r + DIRS[i_dir].r, curr.c + DIRS[i_dir].c};
147
+
148
+ if(is_valid_point(next_p) &&
149
+ !is_impassable_grid_static[next_p.r][next_p.c] &&
150
+ !bfs_visited_grid[next_p.r][next_p.c]){
151
+
152
+ bool is_tentative_wall_conflict = false;
153
+ for(const auto& tw : current_turn_tentative_walls) {
154
+ if(next_p == tw) {
155
+ is_tentative_wall_conflict = true;
156
+ break;
157
+ }
158
+ }
159
+ if(is_tentative_wall_conflict) continue;
160
+
161
+ bfs_visited_grid[next_p.r][next_p.c] = true;
162
+ bfs_parent_grid[next_p.r][next_p.c] = curr;
163
+
164
+ if (next_p == target_pos) {
165
+ path_found_dest = next_p;
166
+ goto bfs_done_label;
167
+ }
168
+ q.push(next_p);
169
+ }
170
+ }
171
+ }
172
+
173
+ bfs_done_label:;
174
+ if (path_found_dest.r == -1) return '.';
175
+
176
+ Point current_step_in_path = path_found_dest;
177
+ while(!(bfs_parent_grid[current_step_in_path.r][current_step_in_path.c] == INVALID_POINT) &&
178
+ !(bfs_parent_grid[current_step_in_path.r][current_step_in_path.c] == start_pos)) {
179
+ current_step_in_path = bfs_parent_grid[current_step_in_path.r][current_step_in_path.c];
180
+ }
181
+
182
+ for(int i_dir = 0; i_dir < 4; ++i_dir){
183
+ if(start_pos.r + DIRS[i_dir].r == current_step_in_path.r &&
184
+ start_pos.c + DIRS[i_dir].c == current_step_in_path.c){
185
+ return DIR_CHARS_MOVE[i_dir];
186
+ }
187
+ }
188
+ return '.';
189
+ }
190
+
191
+
192
+ void initialize_game() {
193
+ std::cin >> N_pets_global;
194
+ pets_global_state.resize(N_pets_global);
195
+ for (int i = 0; i < N_pets_global; ++i) {
196
+ pets_global_state[i].id = i;
197
+ std::cin >> pets_global_state[i].pos.r >> pets_global_state[i].pos.c >> pets_global_state[i].type;
198
+ }
199
+
200
+ std::cin >> M_humans_global;
201
+ humans_global_state.resize(M_humans_global);
202
+
203
+ for(int r_grid=0; r_grid <= GRID_SIZE; ++r_grid) for(int c_grid=0; c_grid <= GRID_SIZE; ++c_grid) is_impassable_grid_static[r_grid][c_grid] = false;
204
+
205
+ int base_strip_height = GRID_SIZE / M_humans_global;
206
+ int remainder_heights = GRID_SIZE % M_humans_global;
207
+ int current_r_start_coord = 1;
208
+
209
+ for (int i = 0; i < M_humans_global; ++i) {
210
+ HumanInfo& human = humans_global_state[i];
211
+ human.id = i;
212
+ std::cin >> human.pos.r >> human.pos.c;
213
+
214
+ int strip_h_for_this_human = base_strip_height + (i < remainder_heights ? 1 : 0);
215
+ human.strip_r_start = current_r_start_coord;
216
+ human.strip_r_end = human.strip_r_start + strip_h_for_this_human - 1;
217
+ human.strip_r_end = std::min(human.strip_r_end, GRID_SIZE);
218
+
219
+ int actual_strip_h = human.strip_r_end - human.strip_r_start + 1;
220
+ int actual_strip_w = GRID_SIZE;
221
+
222
+ human.inner_safe_ul.r = human.strip_r_start + (actual_strip_h >= 3 ? 1 : 0);
223
+ human.inner_safe_ul.c = 1 + (actual_strip_w >= 3 ? 1 : 0);
224
+ human.inner_safe_br.r = human.strip_r_end - (actual_strip_h >= 3 ? 1 : 0);
225
+ human.inner_safe_br.c = GRID_SIZE - (actual_strip_w >= 3 ? 1 : 0);
226
+
227
+ if (human.inner_safe_ul.r > human.inner_safe_br.r) human.inner_safe_br.r = human.inner_safe_ul.r;
228
+ if (human.inner_safe_ul.c > human.inner_safe_br.c) human.inner_safe_br.c = human.inner_safe_ul.c;
229
+
230
+ human.final_stand_pos = {
231
+ human.inner_safe_ul.r + (human.inner_safe_br.r - human.inner_safe_ul.r) / 2,
232
+ human.inner_safe_ul.c + (human.inner_safe_br.c - human.inner_safe_ul.c) / 2
233
+ };
234
+ human.final_stand_pos.r = std::max(human.inner_safe_ul.r, std::min(human.inner_safe_br.r, human.final_stand_pos.r));
235
+ human.final_stand_pos.c = std::max(human.inner_safe_ul.c, std::min(human.inner_safe_br.c, human.final_stand_pos.c));
236
+ if (!is_valid_point(human.final_stand_pos)) {
237
+ human.final_stand_pos = {human.strip_r_start, 1};
238
+ }
239
+
240
+ human.assigned_wall_cells.clear();
241
+ int r_s = human.strip_r_start;
242
+ int r_e = human.strip_r_end;
243
+
244
+ if (i == 0) {
245
+ for (int c_coord = 1; c_coord <= GRID_SIZE; ++c_coord) human.assigned_wall_cells.push_back({r_s, c_coord});
246
+ } else {
247
+ for (int c_coord = GRID_SIZE / 2 + 1; c_coord <= GRID_SIZE; ++c_coord) human.assigned_wall_cells.push_back({r_s, c_coord});
248
+ }
249
+ if (i == M_humans_global - 1) {
250
+ for (int c_coord = 1; c_coord <= GRID_SIZE; ++c_coord) human.assigned_wall_cells.push_back({r_e, c_coord});
251
+ } else {
252
+ for (int c_coord = 1; c_coord <= GRID_SIZE / 2; ++c_coord) human.assigned_wall_cells.push_back({r_e, c_coord});
253
+ }
254
+ for (int r_mid = r_s + 1; r_mid <= r_e - 1; ++r_mid) {
255
+ human.assigned_wall_cells.push_back({r_mid, 1});
256
+ human.assigned_wall_cells.push_back({r_mid, GRID_SIZE});
257
+ }
258
+
259
+ std::sort(human.assigned_wall_cells.begin(), human.assigned_wall_cells.end());
260
+ human.assigned_wall_cells.erase(
261
+ std::unique(human.assigned_wall_cells.begin(), human.assigned_wall_cells.end()),
262
+ human.assigned_wall_cells.end()
263
+ );
264
+ current_r_start_coord = human.strip_r_end + 1;
265
+ }
266
+ }
267
+
268
+ std::string decide_human_actions() {
269
+ std::string actions_str(M_humans_global, '.');
270
+ std::vector<Point> tentative_walls_this_turn;
271
+ std::vector<Point> tentative_move_targets_this_turn(M_humans_global, INVALID_POINT);
272
+
273
+ for (int i = 0; i < M_humans_global; ++i) {
274
+ HumanInfo& human = humans_global_state[i];
275
+
276
+ int unbuilt_walls_count = 0;
277
+ for (const auto& wall_cell : human.assigned_wall_cells) {
278
+ if (is_valid_point(wall_cell) && !is_impassable_grid_static[wall_cell.r][wall_cell.c]) {
279
+ unbuilt_walls_count++;
280
+ }
281
+ }
282
+
283
+ if (unbuilt_walls_count == 0) {
284
+ human.objective = (human.pos == human.final_stand_pos) ?
285
+ HumanObjective::STAYING_IN_SAFE_SPOT :
286
+ HumanObjective::GOING_TO_SAFE_SPOT;
287
+ } else {
288
+ human.objective = HumanObjective::BUILDING_WALLS;
289
+ }
290
+
291
+ if(human.objective == HumanObjective::BUILDING_WALLS && human.turns_stuck_building >= MAX_STUCK_TURNS) {
292
+ human.objective = HumanObjective::REPOSITIONING_STUCK;
293
+ }
294
+
295
+ char chosen_action_for_human_i = '.';
296
+ if (human.objective == HumanObjective::STAYING_IN_SAFE_SPOT) {
297
+ chosen_action_for_human_i = '.';
298
+ } else if (human.objective == HumanObjective::GOING_TO_SAFE_SPOT ||
299
+ human.objective == HumanObjective::REPOSITIONING_STUCK) {
300
+ if(human.objective == HumanObjective::REPOSITIONING_STUCK) human.turns_stuck_building = 0;
301
+
302
+ chosen_action_for_human_i = get_bfs_move_char(human.pos, human.final_stand_pos, tentative_walls_this_turn);
303
+
304
+ } else if (human.objective == HumanObjective::BUILDING_WALLS) {
305
+ Point best_wall_target = INVALID_POINT;
306
+ Point best_stand_point = INVALID_POINT;
307
+ int min_eval_score = INF;
308
+
309
+ for (const auto& wall_coord : human.assigned_wall_cells) {
310
+ if (!is_valid_point(wall_coord) || is_impassable_grid_static[wall_coord.r][wall_coord.c]) continue;
311
+ if (!can_theoretically_build_at(wall_coord, human.id)) continue;
312
+
313
+ int adj_wall_bonus_val = count_adjacent_walls_or_boundaries(wall_coord) * ADJACENT_WALL_PRIORITY_BONUS;
314
+ int current_near_pet_penalty = 0; // NEAR_PET_PENALTY_POINTS_PER_PET is 0
315
+
316
+ for (int k_dir_idx = 0; k_dir_idx < 4; ++k_dir_idx) {
317
+ Point potential_stand_pos = {wall_coord.r + DIRS[k_dir_idx].r,
318
+ wall_coord.c + DIRS[k_dir_idx].c};
319
+
320
+ if (!is_valid_point(potential_stand_pos) || is_impassable_grid_static[potential_stand_pos.r][potential_stand_pos.c]) continue;
321
+
322
+ bool conflict_with_tentative_wall_build_spot = false;
323
+ for(const auto& tw : tentative_walls_this_turn) { if(potential_stand_pos == tw) { conflict_with_tentative_wall_build_spot = true; break; }}
324
+ if(conflict_with_tentative_wall_build_spot) continue;
325
+
326
+ bool conflict_with_tentative_move_dest = false;
327
+ for(int j=0; j < i; ++j) {
328
+ if (tentative_move_targets_this_turn[j] == potential_stand_pos) { conflict_with_tentative_move_dest = true; break; }
329
+ }
330
+ if (conflict_with_tentative_move_dest) continue;
331
+
332
+ int current_dist_to_stand = manhattan_distance(human.pos, potential_stand_pos);
333
+ int current_eval_score = current_dist_to_stand - adj_wall_bonus_val + current_near_pet_penalty;
334
+
335
+ bool is_inside_inner_safe_region =
336
+ (potential_stand_pos.r >= human.inner_safe_ul.r &&
337
+ potential_stand_pos.r <= human.inner_safe_br.r &&
338
+ potential_stand_pos.c >= human.inner_safe_ul.c &&
339
+ potential_stand_pos.c <= human.inner_safe_br.c);
340
+
341
+ if (!is_inside_inner_safe_region) {
342
+ current_eval_score += STAND_OUTSIDE_INNER_SAFE_PENALTY;
343
+ }
344
+
345
+ if (current_eval_score < min_eval_score) {
346
+ min_eval_score = current_eval_score;
347
+ best_wall_target = wall_coord;
348
+ best_stand_point = potential_stand_pos;
349
+ } else if (current_eval_score == min_eval_score) {
350
+ if (best_wall_target.r == -1 ||
351
+ wall_coord < best_wall_target ||
352
+ (wall_coord == best_wall_target && potential_stand_pos < best_stand_point)) {
353
+ best_wall_target = wall_coord;
354
+ best_stand_point = potential_stand_pos;
355
+ }
356
+ }
357
+ }
358
+ }
359
+
360
+ if (best_wall_target.r != -1) {
361
+ human.turns_stuck_building = 0;
362
+ if (human.pos == best_stand_point) {
363
+ for(int k_dir=0; k_dir<4; ++k_dir){
364
+ if(human.pos.r + DIRS[k_dir].r == best_wall_target.r &&
365
+ human.pos.c + DIRS[k_dir].c == best_wall_target.c){
366
+ chosen_action_for_human_i = DIR_CHARS_BUILD[k_dir];
367
+ break;
368
+ }
369
+ }
370
+ } else {
371
+ chosen_action_for_human_i = get_bfs_move_char(human.pos, best_stand_point, tentative_walls_this_turn);
372
+ }
373
+ } else {
374
+ if (unbuilt_walls_count > 0) {
375
+ human.turns_stuck_building++;
376
+ }
377
+ if (human.pos != human.final_stand_pos) {
378
+ chosen_action_for_human_i = get_bfs_move_char(human.pos, human.final_stand_pos, tentative_walls_this_turn);
379
+ } else {
380
+ chosen_action_for_human_i = '.';
381
+ }
382
+ }
383
+ }
384
+
385
+ actions_str[i] = chosen_action_for_human_i;
386
+
387
+ if (chosen_action_for_human_i != '.' && (chosen_action_for_human_i == 'u' || chosen_action_for_human_i == 'd' || chosen_action_for_human_i == 'l' || chosen_action_for_human_i == 'r')) {
388
+ for(int k_dir=0; k_dir<4; ++k_dir) {
389
+ if (chosen_action_for_human_i == DIR_CHARS_BUILD[k_dir]) {
390
+ Point built_wall_pos = {human.pos.r + DIRS[k_dir].r, human.pos.c + DIRS[k_dir].c};
391
+ if (is_valid_point(built_wall_pos)) {
392
+ tentative_walls_this_turn.push_back(built_wall_pos);
393
+ }
394
+ break;
395
+ }
396
+ }
397
+ } else if (chosen_action_for_human_i != '.' && (chosen_action_for_human_i == 'U' || chosen_action_for_human_i == 'D' || chosen_action_for_human_i == 'L' || chosen_action_for_human_i == 'R')) {
398
+ for(int k_dir=0; k_dir<4; ++k_dir) {
399
+ if (chosen_action_for_human_i == DIR_CHARS_MOVE[k_dir]) {
400
+ Point target_pos = {human.pos.r + DIRS[k_dir].r, human.pos.c + DIRS[k_dir].c};
401
+ if (is_valid_point(target_pos)) {
402
+ tentative_move_targets_this_turn[i] = target_pos;
403
+ } else {
404
+ actions_str[i] = '.';
405
+ }
406
+ break;
407
+ }
408
+ }
409
+ }
410
+ }
411
+
412
+ for (int i = 0; i < M_humans_global; ++i) {
413
+ if (actions_str[i] != '.' && (actions_str[i] == 'U' || actions_str[i] == 'D' || actions_str[i] == 'L' || actions_str[i] == 'R')) {
414
+ Point target_move_sq = tentative_move_targets_this_turn[i];
415
+ if (target_move_sq.r == -1) {
416
+ actions_str[i] = '.';
417
+ continue;
418
+ }
419
+
420
+ bool conflict_with_wall = false;
421
+ for (const auto& wall_being_built : tentative_walls_this_turn) {
422
+ if (target_move_sq == wall_being_built) {
423
+ conflict_with_wall = true;
424
+ break;
425
+ }
426
+ }
427
+ if (conflict_with_wall) {
428
+ actions_str[i] = '.';
429
+ } else {
430
+ for (int j = 0; j < i; ++j) {
431
+ if (actions_str[j] != '.' && (actions_str[j] == 'U' || actions_str[j] == 'D' || actions_str[j] == 'L' || actions_str[j] == 'R') &&
432
+ tentative_move_targets_this_turn[j] == target_move_sq) {
433
+ actions_str[i] = '.';
434
+ break;
435
+ }
436
+ }
437
+ }
438
+ }
439
+ }
440
+ return actions_str;
441
+ }
442
+
443
+ void apply_actions_and_update_state(const std::string& actions_str_final) {
444
+ for (int i = 0; i < M_humans_global; ++i) {
445
+ char action = actions_str_final[i];
446
+ HumanInfo& human = humans_global_state[i];
447
+ if (action != '.' && (action == 'u' || action == 'd' || action == 'l' || action == 'r')) {
448
+ for(int k_dir=0; k_dir<4; ++k_dir){
449
+ if (action == DIR_CHARS_BUILD[k_dir]) {
450
+ Point wall_pos = {human.pos.r + DIRS[k_dir].r, human.pos.c + DIRS[k_dir].c};
451
+ if (is_valid_point(wall_pos) && !is_impassable_grid_static[wall_pos.r][wall_pos.c]) {
452
+ is_impassable_grid_static[wall_pos.r][wall_pos.c] = true;
453
+ }
454
+ break;
455
+ }
456
+ }
457
+ }
458
+ }
459
+
460
+ for (int i = 0; i < M_humans_global; ++i) {
461
+ char action = actions_str_final[i];
462
+ HumanInfo& human = humans_global_state[i];
463
+ if (action != '.' && (action == 'U' || action == 'D' || action == 'L' || action == 'R')) {
464
+ for(int k_dir=0; k_dir<4; ++k_dir){
465
+ if (action == DIR_CHARS_MOVE[k_dir]) {
466
+ Point next_pos = {human.pos.r + DIRS[k_dir].r, human.pos.c + DIRS[k_dir].c};
467
+ if (is_valid_point(next_pos) && !is_impassable_grid_static[next_pos.r][next_pos.c]) {
468
+ human.pos = next_pos;
469
+ }
470
+ break;
471
+ }
472
+ }
473
+ }
474
+ }
475
+
476
+ for (int i = 0; i < N_pets_global; ++i) {
477
+ std::string pet_moves_str;
478
+ std::cin >> pet_moves_str;
479
+ if (pet_moves_str == ".") continue;
480
+
481
+ for (char move_char : pet_moves_str) {
482
+ for(int k_dir=0; k_dir<4; ++k_dir){
483
+ if(move_char == PET_MOVE_CHARS[k_dir]){
484
+ pets_global_state[i].pos.r += DIRS[k_dir].r;
485
+ pets_global_state[i].pos.c += DIRS[k_dir].c;
486
+ break;
487
+ }
488
+ }
489
+ }
490
+ }
491
+ }
492
+
493
+ int main() {
494
+ std::ios_base::sync_with_stdio(false);
495
+ std::cin.tie(NULL);
496
+
497
+ initialize_game();
498
+
499
+ for (int turn_idx = 0; turn_idx < NUM_TURNS; ++turn_idx) {
500
+ std::string actions_to_perform = decide_human_actions();
501
+ std::cout << actions_to_perform << std::endl;
502
+
503
+ apply_actions_and_update_state(actions_to_perform);
504
+ }
505
+
506
+ return 0;
507
+ }
508
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc011/best_program.cpp ADDED
@@ -0,0 +1,730 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include <string>
5
+ #include <array>
6
+ #include <algorithm>
7
+ #include <unordered_map>
8
+ #include <map> // For A* visited set
9
+ #include <iomanip>
10
+ #include <chrono>
11
+ #include <functional> // For std::hash
12
+ #include <cmath> // For std::round
13
+ #include <random> // For std::mt19937
14
+ #include <numeric> // For std::iota
15
+ #include <queue> // For A* search (priority_queue)
16
+
17
+ // Constants for tile connections
18
+ const int LEFT_MASK = 1;
19
+ const int UP_MASK = 2;
20
+ const int RIGHT_MASK = 4;
21
+ const int DOWN_MASK = 8;
22
+
23
+ // Max N value, actual N read from input
24
+ const int N_MAX_CONST = 10;
25
+ int N_actual; // Actual N for the current test case
26
+ int T_param; // Actual T for the current test case
27
+
28
+ const int DR_TILE_RELATIVE_TO_EMPTY[] = {-1, 1, 0, 0};
29
+ const int DC_TILE_RELATIVE_TO_EMPTY[] = {0, 0, -1, 1};
30
+ const char MOVE_CHARS[] = {'U', 'D', 'L', 'R'};
31
+
32
+
33
+ std::mt19937 zobrist_rng_engine(123456789);
34
+ std::uniform_int_distribution<uint64_t> distrib_uint64;
35
+ uint64_t zobrist_tile_keys[N_MAX_CONST][N_MAX_CONST][16];
36
+
37
+ // Fast hex char -> int lookup
38
+ int CHAR_TO_VAL[256];
39
+ inline void init_char_to_val() {
40
+ for (int i = 0; i < 256; ++i) CHAR_TO_VAL[i] = 0;
41
+ for (int d = 0; d <= 9; ++d) CHAR_TO_VAL['0' + d] = d;
42
+ for (int d = 0; d < 6; ++d) {
43
+ CHAR_TO_VAL['a' + d] = 10 + d;
44
+ CHAR_TO_VAL['A' + d] = 10 + d;
45
+ }
46
+ }
47
+
48
+
49
+ void init_zobrist_keys() {
50
+ for (int i = 0; i < N_actual; ++i) {
51
+ for (int j = 0; j < N_actual; ++j) {
52
+ for (int k = 0; k < 16; ++k) {
53
+ zobrist_tile_keys[i][j][k] = distrib_uint64(zobrist_rng_engine);
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ int hex_char_to_int(char c) {
60
+ if (c >= '0' && c <= '9') return c - '0';
61
+ return c - 'a' + 10;
62
+ }
63
+
64
+
65
+ struct Board {
66
+ std::array<std::array<char, N_MAX_CONST>, N_MAX_CONST> tiles;
67
+ int empty_r, empty_c;
68
+ uint64_t zobrist_hash_value;
69
+
70
+ Board() : empty_r(0), empty_c(0), zobrist_hash_value(0) {}
71
+
72
+ void calculate_initial_hash() {
73
+ zobrist_hash_value = 0;
74
+ for (int i = 0; i < N_actual; ++i) {
75
+ for (int j = 0; j < N_actual; ++j) {
76
+ zobrist_hash_value ^= zobrist_tile_keys[i][j][CHAR_TO_VAL[(unsigned char)tiles[i][j]]];
77
+ }
78
+ }
79
+ }
80
+
81
+ void update_hash_after_move(int pos_tile_becomes_empty_r, int pos_tile_becomes_empty_c,
82
+ int pos_empty_gets_tile_r, int pos_empty_gets_tile_c) {
83
+ int moved_tile_val_int = hex_char_to_int(tiles[pos_empty_gets_tile_r][pos_empty_gets_tile_c]);
84
+
85
+ zobrist_hash_value ^= zobrist_tile_keys[pos_tile_becomes_empty_r][pos_tile_becomes_empty_c][moved_tile_val_int];
86
+ zobrist_hash_value ^= zobrist_tile_keys[pos_empty_gets_tile_r][pos_empty_gets_tile_c][0];
87
+
88
+ zobrist_hash_value ^= zobrist_tile_keys[pos_tile_becomes_empty_r][pos_tile_becomes_empty_c][0];
89
+ zobrist_hash_value ^= zobrist_tile_keys[pos_empty_gets_tile_r][pos_empty_gets_tile_c][moved_tile_val_int];
90
+ }
91
+
92
+ bool apply_move_char(char move_char) {
93
+ int move_dir_idx = -1;
94
+ for(int i=0; i<4; ++i) if(MOVE_CHARS[i] == move_char) move_dir_idx = i;
95
+
96
+ if(move_dir_idx == -1) return false;
97
+
98
+ int tile_to_move_r = empty_r + DR_TILE_RELATIVE_TO_EMPTY[move_dir_idx];
99
+ int tile_to_move_c = empty_c + DC_TILE_RELATIVE_TO_EMPTY[move_dir_idx];
100
+
101
+ if (tile_to_move_r < 0 || tile_to_move_r >= N_actual || tile_to_move_c < 0 || tile_to_move_c >= N_actual) {
102
+ return false;
103
+ }
104
+
105
+ char moved_tile_hex_val = tiles[tile_to_move_r][tile_to_move_c];
106
+ tiles[empty_r][empty_c] = moved_tile_hex_val;
107
+ tiles[tile_to_move_r][tile_to_move_c] = '0';
108
+
109
+ update_hash_after_move(tile_to_move_r, tile_to_move_c, empty_r, empty_c);
110
+
111
+ empty_r = tile_to_move_r;
112
+ empty_c = tile_to_move_c;
113
+ return true;
114
+ }
115
+ };
116
+
117
+
118
+ struct ScoreComponents {
119
+ int max_tree_size;
120
+ int num_components;
121
+ };
122
+ std::unordered_map<uint64_t, ScoreComponents> s_value_cache_by_hash;
123
+ const size_t MAX_SCORE_CACHE_SIZE_CONST = 2000000;
124
+
125
+ struct DSU {
126
+ std::vector<int> parent;
127
+ std::vector<int> nodes_in_set;
128
+ std::vector<int> edges_in_set;
129
+ int N_sq_total_cells;
130
+
131
+ DSU(int current_N) : N_sq_total_cells(current_N * current_N) {
132
+ parent.resize(N_sq_total_cells);
133
+ std::iota(parent.begin(), parent.end(), 0);
134
+ nodes_in_set.assign(N_sq_total_cells, 0);
135
+ edges_in_set.assign(N_sq_total_cells, 0);
136
+ }
137
+
138
+ int find(int i) {
139
+ if (parent[i] == i)
140
+ return i;
141
+ return parent[i] = find(parent[i]);
142
+ }
143
+
144
+ void unite(int i_idx, int j_idx) {
145
+ int root_i = find(i_idx);
146
+ int root_j = find(j_idx);
147
+
148
+ if (nodes_in_set[root_i] < nodes_in_set[root_j]) std::swap(root_i, root_j);
149
+
150
+ parent[root_j] = root_i;
151
+ nodes_in_set[root_i] += nodes_in_set[root_j];
152
+ edges_in_set[root_i] += edges_in_set[root_j];
153
+ }
154
+
155
+ void add_edge(int u_idx, int v_idx) {
156
+ int root_u = find(u_idx);
157
+ int root_v = find(v_idx);
158
+ if (root_u != root_v) {
159
+ unite(u_idx, v_idx);
160
+ edges_in_set[find(u_idx)]++;
161
+ } else {
162
+ edges_in_set[root_u]++;
163
+ }
164
+ }
165
+ };
166
+
167
+
168
+ ScoreComponents calculate_scores(const Board& board) {
169
+ auto it_cache = s_value_cache_by_hash.find(board.zobrist_hash_value);
170
+ if (it_cache != s_value_cache_by_hash.end()) {
171
+ return it_cache->second;
172
+ }
173
+
174
+ DSU dsu(N_actual);
175
+
176
+ for (int r = 0; r < N_actual; ++r) {
177
+ for (int c = 0; c < N_actual; ++c) {
178
+ int cell_idx = r * N_actual + c;
179
+ if (board.tiles[r][c] != '0') {
180
+ dsu.nodes_in_set[cell_idx] = 1;
181
+ } else {
182
+ dsu.nodes_in_set[cell_idx] = 0;
183
+ }
184
+ }
185
+ }
186
+
187
+ for (int r = 0; r < N_actual; ++r) {
188
+ for (int c = 0; c < N_actual - 1; ++c) {
189
+ int tile1_val = CHAR_TO_VAL[(unsigned char)board.tiles[r][c]];
190
+ int tile2_val = CHAR_TO_VAL[(unsigned char)board.tiles[r][c+1]];
191
+ if (tile1_val && tile2_val && (tile1_val & RIGHT_MASK) && (tile2_val & LEFT_MASK)) {
192
+ dsu.add_edge(r * N_actual + c, r * N_actual + (c + 1));
193
+ }
194
+ }
195
+ }
196
+ for (int r = 0; r < N_actual - 1; ++r) {
197
+ for (int c = 0; c < N_actual; ++c) {
198
+ int tile1_val = CHAR_TO_VAL[(unsigned char)board.tiles[r][c]];
199
+ int tile2_val = CHAR_TO_VAL[(unsigned char)board.tiles[r+1][c]];
200
+ if (tile1_val && tile2_val && (tile1_val & DOWN_MASK) && (tile2_val & UP_MASK)) {
201
+ dsu.add_edge(r * N_actual + c, (r + 1) * N_actual + c);
202
+ }
203
+ }
204
+ }
205
+
206
+ int max_tree_size = 0;
207
+ int total_num_components = 0;
208
+
209
+ for (int i = 0; i < dsu.N_sq_total_cells; ++i) {
210
+ if (dsu.parent[i] == i && dsu.nodes_in_set[i] > 0) {
211
+ total_num_components++;
212
+ if (dsu.edges_in_set[i] == dsu.nodes_in_set[i] - 1) {
213
+ if (dsu.nodes_in_set[i] > max_tree_size) {
214
+ max_tree_size = dsu.nodes_in_set[i];
215
+ }
216
+ }
217
+ }
218
+ }
219
+
220
+ ScoreComponents result = {max_tree_size, total_num_components};
221
+ if (s_value_cache_by_hash.size() < MAX_SCORE_CACHE_SIZE_CONST) {
222
+ s_value_cache_by_hash[board.zobrist_hash_value] = result;
223
+ }
224
+ return result;
225
+ }
226
+
227
+
228
+ int TARGET_EMPTY_R_GLOBAL_FOR_A_STAR, TARGET_EMPTY_C_GLOBAL_FOR_A_STAR; // Used by A* heuristic
229
+ bool A_STAR_PHASE_WAS_RUN = false; // Flag to adjust beam score empty penalty
230
+
231
+ double calculate_beam_score(const ScoreComponents& scores, int K_total, const Board& current_board_state) {
232
+ int S = scores.max_tree_size;
233
+
234
+ const double FULL_TREE_BASE_SCORE = 1e18;
235
+ if (S == N_actual * N_actual - 1) {
236
+ return FULL_TREE_BASE_SCORE + (double)(T_param * 2 - K_total);
237
+ }
238
+
239
+ double W_S = 1e9;
240
+ double W_NC = W_S * 0.6; // Slightly reduce component penalty to favor growing S faster.
241
+ double W_K = 1.0;
242
+ double W_empty_dist_penalty_main;
243
+
244
+ if (A_STAR_PHASE_WAS_RUN) { // A* moved empty to target initially
245
+ W_empty_dist_penalty_main = W_K * 0.5; // Very low penalty, allow free movement
246
+ } else { // Empty started at target, or A* failed (should not happen)
247
+ W_empty_dist_penalty_main = W_K * 10.0; // Moderate penalty
248
+ }
249
+
250
+ double score_val = (double)S * W_S;
251
+ if (scores.num_components > 1) {
252
+ score_val -= (double)(scores.num_components - 1) * W_NC;
253
+ } else if (scores.num_components == 0 && N_actual * N_actual - 1 > 0) {
254
+ score_val -= (double)(N_actual * N_actual -1) * W_NC;
255
+ }
256
+
257
+ // Bonus for being very close to a full tree and connected
258
+ if (S >= (N_actual * N_actual - 1) - 2 && scores.num_components == 1 && S < N_actual * N_actual - 1) {
259
+ score_val += W_S * 0.5; // Significant bonus to encourage the last step
260
+ }
261
+
262
+ score_val -= (double)K_total * W_K;
263
+
264
+ // Penalty for empty square relative to (N-1,N-1)
265
+ int dist_empty_to_corner = std::abs(current_board_state.empty_r - (N_actual - 1)) +
266
+ std::abs(current_board_state.empty_c - (N_actual - 1));
267
+ score_val -= dist_empty_to_corner * W_empty_dist_penalty_main;
268
+
269
+ return score_val;
270
+ }
271
+
272
+ double calculate_actual_score(int S, int K_total) {
273
+ if (N_actual * N_actual - 1 == 0) return 0;
274
+ if (S == N_actual * N_actual - 1) {
275
+ if (K_total > T_param) return 0;
276
+ return std::round(500000.0 * (2.0 - (double)K_total / T_param));
277
+ } else {
278
+ return std::round(500000.0 * (double)S / (N_actual * N_actual - 1.0));
279
+ }
280
+ }
281
+
282
+ /* Function: count_matched_edge_pair
283
+ Doc: Returns 1 if two adjacent cells form a valid connection (L-R or U-D), else 0.
284
+ Assumes (r1,c1) and (r2,c2) differ by exactly 1 in Manhattan distance.
285
+ */
286
+ inline int count_matched_edge_pair(const Board& b, int r1, int c1, int r2, int c2) {
287
+ if (r1 == r2) {
288
+ if (c1 > c2) std::swap(c1, c2);
289
+ if (c2 != c1 + 1) return 0;
290
+ int v1 = CHAR_TO_VAL[(unsigned char)b.tiles[r1][c1]];
291
+ int v2 = CHAR_TO_VAL[(unsigned char)b.tiles[r2][c2]];
292
+ if (!v1 || !v2) return 0;
293
+ return ((v1 & RIGHT_MASK) && (v2 & LEFT_MASK)) ? 1 : 0;
294
+ } else if (c1 == c2) {
295
+ if (r1 > r2) std::swap(r1, r2);
296
+ if (r2 != r1 + 1) return 0;
297
+ int v1 = CHAR_TO_VAL[(unsigned char)b.tiles[r1][c1]];
298
+ int v2 = CHAR_TO_VAL[(unsigned char)b.tiles[r2][c2]];
299
+ if (!v1 || !v2) return 0;
300
+ return ((v1 & DOWN_MASK) && (v2 & UP_MASK)) ? 1 : 0;
301
+ }
302
+ return 0;
303
+ }
304
+
305
+ /* Function: count_cell_matched_degree
306
+ Doc: Counts the number of matched edges incident to a given cell (r,c).
307
+ */
308
+ inline int count_cell_matched_degree(const Board& b, int r, int c) {
309
+ int deg = 0;
310
+ if (r > 0) deg += count_matched_edge_pair(b, r - 1, c, r, c);
311
+ if (r + 1 < N_actual) deg += count_matched_edge_pair(b, r, c, r + 1, c);
312
+ if (c > 0) deg += count_matched_edge_pair(b, r, c - 1, r, c);
313
+ if (c + 1 < N_actual) deg += count_matched_edge_pair(b, r, c, r, c + 1);
314
+ return deg;
315
+ }
316
+
317
+ /* Function: compute_total_matched_edges
318
+ Doc: Counts all matched undirected edges on the board by scanning right and down neighbors.
319
+ */
320
+ inline int compute_total_matched_edges(const Board& b) {
321
+ int cnt = 0;
322
+ for (int r = 0; r < N_actual; ++r) {
323
+ for (int c = 0; c + 1 < N_actual; ++c) {
324
+ cnt += count_matched_edge_pair(b, r, c, r, c + 1);
325
+ }
326
+ }
327
+ for (int r = 0; r + 1 < N_actual; ++r) {
328
+ for (int c = 0; c < N_actual; ++c) {
329
+ cnt += count_matched_edge_pair(b, r, c, r + 1, c);
330
+ }
331
+ }
332
+ return cnt;
333
+ }
334
+
335
+ struct BeamHistoryEntry {
336
+ int parent_history_idx;
337
+ char move_char_taken;
338
+ };
339
+ std::vector<BeamHistoryEntry> beam_history_storage;
340
+ const size_t MAX_BEAM_HISTORY_STORAGE_SIZE_CONST = 3000000;
341
+
342
+ struct BeamState {
343
+ Board board;
344
+ double beam_score_val;
345
+ int k_beam_moves;
346
+ int history_idx;
347
+ int prev_move_direction_idx;
348
+ int approx_edges; // heuristic: number of matched undirected edges
349
+
350
+ bool operator<(const BeamState& other) const {
351
+ if (beam_score_val != other.beam_score_val) return beam_score_val > other.beam_score_val;
352
+ return approx_edges > other.approx_edges;
353
+ }
354
+ };
355
+
356
+ struct CandidateLight {
357
+ // Doc: Lightweight candidate used to pre-filter by approximate edge count before expensive scoring.
358
+ Board board;
359
+ int approx_edges;
360
+ int k_beam_moves;
361
+ int history_idx;
362
+ int prev_move_direction_idx;
363
+ bool operator<(const CandidateLight& other) const {
364
+ return approx_edges > other.approx_edges; // sort descending by approx_edges
365
+ }
366
+ };
367
+
368
+ std::chrono::steady_clock::time_point T_START_CHRONO_MAIN;
369
+ const int TIME_LIMIT_MS_SLACK_CONST = 400; // Universal slack
370
+ long long TIME_LIMIT_MS_EFFECTIVE_MAIN;
371
+
372
+
373
+ std::mt19937 rng_stochastic_selection_main;
374
+ std::unordered_map<uint64_t, int> min_K_to_reach_by_hash_main;
375
+ const size_t MAX_MIN_K_CACHE_SIZE_CONST = 2000000;
376
+
377
+
378
+ struct AStarEmptyState {
379
+ int r, c;
380
+ int g_cost;
381
+ std::string path;
382
+
383
+ bool operator>(const AStarEmptyState& other) const {
384
+ int h_cost_this = std::abs(r - TARGET_EMPTY_R_GLOBAL_FOR_A_STAR) + std::abs(c - TARGET_EMPTY_C_GLOBAL_FOR_A_STAR);
385
+ int h_cost_other = std::abs(other.r - TARGET_EMPTY_R_GLOBAL_FOR_A_STAR) + std::abs(other.c - TARGET_EMPTY_C_GLOBAL_FOR_A_STAR);
386
+ if (g_cost + h_cost_this != other.g_cost + h_cost_other) {
387
+ return g_cost + h_cost_this > other.g_cost + h_cost_other;
388
+ }
389
+ return g_cost > other.g_cost;
390
+ }
391
+ };
392
+
393
+ std::string find_path_for_empty(const Board& initial_board_state_for_A_star, int target_r, int target_c) {
394
+ TARGET_EMPTY_R_GLOBAL_FOR_A_STAR = target_r;
395
+ TARGET_EMPTY_C_GLOBAL_FOR_A_STAR = target_c;
396
+
397
+ std::priority_queue<AStarEmptyState, std::vector<AStarEmptyState>, std::greater<AStarEmptyState>> pq;
398
+ std::vector<std::vector<int>> min_g_cost_grid(N_actual, std::vector<int>(N_actual, T_param + 1));
399
+
400
+ pq.push({initial_board_state_for_A_star.empty_r, initial_board_state_for_A_star.empty_c, 0, ""});
401
+ min_g_cost_grid[initial_board_state_for_A_star.empty_r][initial_board_state_for_A_star.empty_c] = 0;
402
+
403
+ int A_star_max_depth = N_actual * N_actual * 2; // Allow more depth just in case
404
+
405
+ while(!pq.empty()){
406
+ AStarEmptyState current = pq.top();
407
+ pq.pop();
408
+
409
+ if (current.g_cost > min_g_cost_grid[current.r][current.c]) {
410
+ continue;
411
+ }
412
+
413
+ if (current.r == target_r && current.c == target_c) {
414
+ return current.path;
415
+ }
416
+
417
+ if (current.g_cost >= A_star_max_depth) continue;
418
+
419
+ for (int move_idx = 0; move_idx < 4; ++move_idx) {
420
+ int tile_that_moves_r = current.r + DR_TILE_RELATIVE_TO_EMPTY[move_idx];
421
+ int tile_that_moves_c = current.c + DC_TILE_RELATIVE_TO_EMPTY[move_idx];
422
+
423
+ if (tile_that_moves_r < 0 || tile_that_moves_r >= N_actual || tile_that_moves_c < 0 || tile_that_moves_c >= N_actual) {
424
+ continue;
425
+ }
426
+
427
+ int next_empty_r = tile_that_moves_r;
428
+ int next_empty_c = tile_that_moves_c;
429
+
430
+ int next_g_cost = current.g_cost + 1;
431
+
432
+ if (min_g_cost_grid[next_empty_r][next_empty_c] <= next_g_cost) {
433
+ continue;
434
+ }
435
+ min_g_cost_grid[next_empty_r][next_empty_c] = next_g_cost;
436
+ pq.push({next_empty_r, next_empty_c, next_g_cost, current.path + MOVE_CHARS[move_idx]});
437
+ }
438
+ }
439
+ return "";
440
+ }
441
+
442
+ std::string reconstruct_beam_path(int final_history_idx) {
443
+ std::string path_str = "";
444
+ int current_trace_hist_idx = final_history_idx;
445
+ while(current_trace_hist_idx > 0 &&
446
+ static_cast<size_t>(current_trace_hist_idx) < beam_history_storage.size() &&
447
+ beam_history_storage[current_trace_hist_idx].parent_history_idx != -1) {
448
+ path_str += beam_history_storage[current_trace_hist_idx].move_char_taken;
449
+ current_trace_hist_idx = beam_history_storage[current_trace_hist_idx].parent_history_idx;
450
+ }
451
+ std::reverse(path_str.begin(), path_str.end());
452
+ return path_str;
453
+ }
454
+
455
+
456
+ int main(int /*argc*/, char** /*argv*/) {
457
+ std::ios_base::sync_with_stdio(false);
458
+ std::cin.tie(NULL);
459
+
460
+ unsigned int random_seed_stochastic = std::chrono::steady_clock::now().time_since_epoch().count();
461
+ rng_stochastic_selection_main.seed(random_seed_stochastic);
462
+
463
+ T_START_CHRONO_MAIN = std::chrono::steady_clock::now();
464
+
465
+ std::cin >> N_actual >> T_param;
466
+ init_char_to_val();
467
+
468
+ init_zobrist_keys();
469
+
470
+ Board current_board_obj;
471
+ for (int i = 0; i < N_actual; ++i) {
472
+ std::string row_str;
473
+ std::cin >> row_str;
474
+ for (int j = 0; j < N_actual; ++j) {
475
+ current_board_obj.tiles[i][j] = row_str[j];
476
+ if (current_board_obj.tiles[i][j] == '0') {
477
+ current_board_obj.empty_r = i;
478
+ current_board_obj.empty_c = j;
479
+ }
480
+ }
481
+ }
482
+ current_board_obj.calculate_initial_hash();
483
+
484
+ std::string initial_empty_moves_path = "";
485
+ // Try routing empty to each corner and pick the one that maximizes our beam score after routing.
486
+ {
487
+ const int cr[4] = {0, 0, N_actual - 1, N_actual - 1};
488
+ const int cc[4] = {0, N_actual - 1, 0, N_actual - 1};
489
+ double best_score = -1e300;
490
+ std::string best_path;
491
+ for (int i = 0; i < 4; ++i) {
492
+ std::string path = find_path_for_empty(current_board_obj, cr[i], cc[i]);
493
+ Board tmp = current_board_obj;
494
+ for (char ch : path) tmp.apply_move_char(ch);
495
+ ScoreComponents sc = calculate_scores(tmp);
496
+ A_STAR_PHASE_WAS_RUN = true; // relax empty-distance penalty after guided routing
497
+ double scv = calculate_beam_score(sc, (int)path.length(), tmp);
498
+ if (scv > best_score) { best_score = scv; best_path = path; }
499
+ }
500
+ initial_empty_moves_path = best_path;
501
+ }
502
+ for (char move_char : initial_empty_moves_path) {
503
+ current_board_obj.apply_move_char(move_char);
504
+ }
505
+ int K_initial_empty_moves = (int)initial_empty_moves_path.length();
506
+
507
+ // Adaptive time limit after A*
508
+ auto time_after_astar = std::chrono::steady_clock::now();
509
+ long long elapsed_astar_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_after_astar - T_START_CHRONO_MAIN).count();
510
+ TIME_LIMIT_MS_EFFECTIVE_MAIN = 2950 - elapsed_astar_ms - TIME_LIMIT_MS_SLACK_CONST;
511
+
512
+
513
+ // Reserve caches (still used by evaluation in MCTS)
514
+ beam_history_storage.reserve(MAX_BEAM_HISTORY_STORAGE_SIZE_CONST);
515
+ s_value_cache_by_hash.reserve(MAX_SCORE_CACHE_SIZE_CONST);
516
+ min_K_to_reach_by_hash_main.reserve(MAX_MIN_K_CACHE_SIZE_CONST);
517
+
518
+ // Initialize best known based on current board (after optional A* to corner)
519
+ ScoreComponents init_score_comp = calculate_scores(current_board_obj);
520
+ double overall_best_actual_score = calculate_actual_score(init_score_comp.max_tree_size, K_initial_empty_moves);
521
+ std::string overall_best_path_str = initial_empty_moves_path;
522
+
523
+ // -------------------------
524
+ // BEAM SEARCH (restored, time-bounded)
525
+ // -------------------------
526
+ // Doc: Deterministic beam search with:
527
+ // - Zobrist-based visited table storing minimal K to reach a hash
528
+ // - Strong primary score on largest tree size, penalties on #components and move count
529
+ // - Tiebreaker using local matched-edge heuristic around the moved tile and the previous empty
530
+ // - Elite retention + stochastic sampling for diversity
531
+ // - Stops on time/memory budget or when T is exhausted
532
+ std::vector<BeamState> current_beam;
533
+
534
+ ScoreComponents initial_scores_for_beam = calculate_scores(current_board_obj);
535
+ double initial_beam_eval_score = calculate_beam_score(initial_scores_for_beam, K_initial_empty_moves, current_board_obj);
536
+
537
+ beam_history_storage.push_back({-1, ' '}); // history idx 0 is sentinel
538
+ current_beam.push_back({current_board_obj, initial_beam_eval_score, 0, 0, -1, compute_total_matched_edges(current_board_obj)});
539
+
540
+ min_K_to_reach_by_hash_main[current_board_obj.zobrist_hash_value] = K_initial_empty_moves;
541
+
542
+ int beam_width;
543
+ float elite_ratio = 0.2f;
544
+ int stochastic_sample_pool_factor = 3;
545
+
546
+ if (N_actual <= 6) { beam_width = 1200;}
547
+ else if (N_actual == 7) { beam_width = 1000;}
548
+ else if (N_actual == 8) { beam_width = 700;}
549
+ else if (N_actual == 9) { beam_width = 400;}
550
+ else { beam_width = 250;}
551
+
552
+ std::vector<BeamState> candidates_pool;
553
+ candidates_pool.reserve(beam_width * 4 + 16);
554
+
555
+ std::vector<BeamState> next_beam_states_temp;
556
+ next_beam_states_temp.reserve(beam_width + 16);
557
+
558
+ std::vector<int> stochastic_selection_indices;
559
+ stochastic_selection_indices.reserve(stochastic_sample_pool_factor * beam_width + 16);
560
+
561
+ int k_iter_count_beam = 0;
562
+
563
+ for (int k_beam_iter = 0; K_initial_empty_moves + k_beam_iter < T_param; ++k_beam_iter) {
564
+ k_iter_count_beam++;
565
+ if (k_iter_count_beam % 10 == 0) {
566
+ long long now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - T_START_CHRONO_MAIN).count();
567
+ if (now_ms > 2950 - TIME_LIMIT_MS_SLACK_CONST) break;
568
+ }
569
+ if (beam_history_storage.size() >= MAX_BEAM_HISTORY_STORAGE_SIZE_CONST - ((size_t)beam_width * 4 + 128)) {
570
+ break;
571
+ }
572
+
573
+ candidates_pool.clear();
574
+
575
+ bool found_full_this_iter = false;
576
+
577
+ for (const auto& current_state_in_beam : current_beam) {
578
+ Board temp_board_for_moves = current_state_in_beam.board;
579
+
580
+ int parent_k_beam = current_state_in_beam.k_beam_moves;
581
+ int parent_history_idx = current_state_in_beam.history_idx;
582
+ int prev_m_dir_idx = current_state_in_beam.prev_move_direction_idx;
583
+
584
+ for (int move_dir_idx = 0; move_dir_idx < 4; ++move_dir_idx) {
585
+ if (prev_m_dir_idx != -1 && ((prev_m_dir_idx ^ 1) == move_dir_idx)) continue;
586
+
587
+ char current_move_char = MOVE_CHARS[move_dir_idx];
588
+ int original_empty_r = temp_board_for_moves.empty_r;
589
+ int original_empty_c = temp_board_for_moves.empty_c;
590
+ uint64_t original_hash = temp_board_for_moves.zobrist_hash_value;
591
+
592
+ int tile_to_move_r = original_empty_r + DR_TILE_RELATIVE_TO_EMPTY[move_dir_idx];
593
+ int tile_to_move_c = original_empty_c + DC_TILE_RELATIVE_TO_EMPTY[move_dir_idx];
594
+
595
+ if (tile_to_move_r < 0 || tile_to_move_r >= N_actual || tile_to_move_c < 0 || tile_to_move_c >= N_actual) {
596
+ continue;
597
+ }
598
+
599
+ // Inline move for speed (swap chars and update hash/coords)
600
+ char moved_tile_hex_val = temp_board_for_moves.tiles[tile_to_move_r][tile_to_move_c];
601
+ temp_board_for_moves.tiles[original_empty_r][original_empty_c] = moved_tile_hex_val;
602
+ temp_board_for_moves.tiles[tile_to_move_r][tile_to_move_c] = '0';
603
+ temp_board_for_moves.empty_r = tile_to_move_r;
604
+ temp_board_for_moves.empty_c = tile_to_move_c;
605
+ temp_board_for_moves.update_hash_after_move(tile_to_move_r, tile_to_move_c, original_empty_r, original_empty_c);
606
+
607
+ int next_k_beam = parent_k_beam + 1;
608
+ int next_K_total = K_initial_empty_moves + next_k_beam;
609
+
610
+ bool already_reached_better = false;
611
+ auto it_map = min_K_to_reach_by_hash_main.find(temp_board_for_moves.zobrist_hash_value);
612
+ if (it_map != min_K_to_reach_by_hash_main.end()) {
613
+ if (it_map->second <= next_K_total) {
614
+ already_reached_better = true;
615
+ } else {
616
+ it_map->second = next_K_total;
617
+ }
618
+ } else {
619
+ if (min_K_to_reach_by_hash_main.size() < MAX_MIN_K_CACHE_SIZE_CONST) {
620
+ min_K_to_reach_by_hash_main[temp_board_for_moves.zobrist_hash_value] = next_K_total;
621
+ }
622
+ }
623
+
624
+ if (already_reached_better) {
625
+ // revert
626
+ temp_board_for_moves.tiles[tile_to_move_r][tile_to_move_c] = moved_tile_hex_val;
627
+ temp_board_for_moves.tiles[original_empty_r][original_empty_c] = '0';
628
+ temp_board_for_moves.empty_r = original_empty_r;
629
+ temp_board_for_moves.empty_c = original_empty_c;
630
+ temp_board_for_moves.zobrist_hash_value = original_hash;
631
+ continue;
632
+ }
633
+
634
+ ScoreComponents next_scores = calculate_scores(temp_board_for_moves);
635
+ if (next_scores.max_tree_size == N_actual * N_actual - 1) found_full_this_iter = true;
636
+ double next_beam_eval_score = calculate_beam_score(next_scores, next_K_total, temp_board_for_moves);
637
+
638
+ beam_history_storage.push_back({parent_history_idx, current_move_char});
639
+ int new_history_idx = (int)beam_history_storage.size() - 1;
640
+
641
+ int approx_local = count_cell_matched_degree(temp_board_for_moves, original_empty_r, original_empty_c)
642
+ + count_cell_matched_degree(temp_board_for_moves, tile_to_move_r, tile_to_move_c);
643
+ candidates_pool.push_back({temp_board_for_moves, next_beam_eval_score, next_k_beam, new_history_idx, move_dir_idx, approx_local});
644
+
645
+ double current_actual_score_val = calculate_actual_score(next_scores.max_tree_size, next_K_total);
646
+ if (current_actual_score_val > overall_best_actual_score) {
647
+ overall_best_actual_score = current_actual_score_val;
648
+ overall_best_path_str = initial_empty_moves_path + reconstruct_beam_path(new_history_idx);
649
+ } else if (current_actual_score_val == overall_best_actual_score) {
650
+ std::string cand = initial_empty_moves_path + reconstruct_beam_path(new_history_idx);
651
+ if (cand.length() < overall_best_path_str.length()) overall_best_path_str = cand;
652
+ }
653
+
654
+ // revert
655
+ temp_board_for_moves.tiles[tile_to_move_r][tile_to_move_c] = moved_tile_hex_val;
656
+ temp_board_for_moves.tiles[original_empty_r][original_empty_c] = '0';
657
+ temp_board_for_moves.empty_r = original_empty_r;
658
+ temp_board_for_moves.empty_c = original_empty_c;
659
+ temp_board_for_moves.zobrist_hash_value = original_hash;
660
+ }
661
+ }
662
+
663
+ if (candidates_pool.empty()) break;
664
+
665
+ if (found_full_this_iter) { break; } // Early exit: earliest full tree yields minimal K in beam
666
+
667
+ std::sort(candidates_pool.begin(), candidates_pool.end());
668
+
669
+ next_beam_states_temp.clear();
670
+ int num_elites = std::min((int)candidates_pool.size(), (int)(beam_width * elite_ratio));
671
+ num_elites = std::max(0, num_elites);
672
+
673
+ for (int i = 0; i < num_elites && i < (int)candidates_pool.size(); ++i) {
674
+ next_beam_states_temp.push_back(candidates_pool[i]);
675
+ }
676
+
677
+ if ((int)next_beam_states_temp.size() < beam_width && (int)candidates_pool.size() > num_elites) {
678
+ stochastic_selection_indices.clear();
679
+ int pool_start_idx = num_elites;
680
+ int pool_end_idx = std::min((int)candidates_pool.size(), num_elites + stochastic_sample_pool_factor * beam_width);
681
+ for (int i = pool_start_idx; i < pool_end_idx; ++i) stochastic_selection_indices.push_back(i);
682
+ if (!stochastic_selection_indices.empty()) {
683
+ std::shuffle(stochastic_selection_indices.begin(), stochastic_selection_indices.end(), rng_stochastic_selection_main);
684
+ }
685
+ for (size_t i = 0; i < stochastic_selection_indices.size() && (int)next_beam_states_temp.size() < beam_width; ++i) {
686
+ next_beam_states_temp.push_back(candidates_pool[stochastic_selection_indices[i]]);
687
+ }
688
+ }
689
+
690
+ current_beam = next_beam_states_temp;
691
+ if (current_beam.empty()) break;
692
+ }
693
+
694
+ // Local refinement: quick greedy hill-climb on the best found solution within remaining time
695
+ auto t_ref_end = T_START_CHRONO_MAIN + std::chrono::milliseconds(2950 - 20);
696
+ Board refine_b = current_board_obj;
697
+ for (char ch : overall_best_path_str) refine_b.apply_move_char(ch);
698
+ int K_now = (int)overall_best_path_str.size();
699
+ ScoreComponents sc_best = calculate_scores(refine_b);
700
+ int edges_best = compute_total_matched_edges(refine_b);
701
+ int last_dir_ref = -1;
702
+ if (!overall_best_path_str.empty()) {
703
+ char lastch = overall_best_path_str.back();
704
+ for (int i = 0; i < 4; ++i) if (MOVE_CHARS[i] == lastch) last_dir_ref = i;
705
+ }
706
+ while (sc_best.max_tree_size < N_actual * N_actual - 1 && K_now < T_param && std::chrono::steady_clock::now() < t_ref_end) {
707
+ int best_mv = -1; int best_S = sc_best.max_tree_size; int best_edges = edges_best;
708
+ // Try all non-backtracking moves and keep the best (lexicographically by S, then edges)
709
+ for (int mv = 0; mv < 4; ++mv) {
710
+ if (last_dir_ref != -1 && (last_dir_ref ^ 1) == mv) continue;
711
+ Board b2 = refine_b;
712
+ if (!b2.apply_move_char(MOVE_CHARS[mv])) continue;
713
+ ScoreComponents sc2 = calculate_scores(b2);
714
+ int e2 = compute_total_matched_edges(b2);
715
+ if (sc2.max_tree_size > best_S || (sc2.max_tree_size == best_S && e2 > best_edges)) {
716
+ best_mv = mv; best_S = sc2.max_tree_size; best_edges = e2;
717
+ }
718
+ }
719
+ if (best_mv == -1) break;
720
+ refine_b.apply_move_char(MOVE_CHARS[best_mv]);
721
+ overall_best_path_str.push_back(MOVE_CHARS[best_mv]);
722
+ sc_best.max_tree_size = best_S;
723
+ edges_best = best_edges;
724
+ last_dir_ref = best_mv;
725
+ ++K_now;
726
+ }
727
+ std::cout << overall_best_path_str << std::endl;
728
+ return 0;
729
+ }
730
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc011/config.yaml ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ALE-Bench ahc011 — AtCoder Heuristic Contest
2
+ # Usage: skydiscover-run initial_program.cpp evaluator.py -c config.yaml -s <strategy>
3
+ language: cpp
4
+ diff_based_generation: true
5
+ max_iterations: 100
6
+ checkpoint_interval: 10
7
+ max_solution_length: 60000
8
+ llm:
9
+ api_base: https://api.openai.com/v1
10
+ models:
11
+ - name: "gpt-5"
12
+ weight: 1.0
13
+ max_tokens: 32000
14
+ timeout: 600
15
+ prompt:
16
+ system_message: "You are a world-class algorithm engineer, and you are very good at programming. Now, you are participating\
17
+ \ in a programming contest. You are asked to solve a heuristic problem, known as an NP-hard problem.\n\nStory\n--------\n\
18
+ Takahashi loves puzzles and is playing with the following famous sliding puzzle.\n> There are $N^2-1$ tiles on an $N \\\
19
+ times N$ board.\n> There is a single empty square, and you can slide an adjacent tile in any of the four directions into\
20
+ \ the empty square.\n> Some picture is divided into each tile. By repeatedly sliding the tiles, please align the picture.\n\
21
+ \nThe trouble is, Takahashi had thrown away the instruction manual, so he lost the target picture.\nAccording to his memory,\
22
+ \ the target picture was a <a href=\"https://en.wikipedia.org/wiki/Tree_(graph_theory)\">tree</a>.\nBy repeating the sliding\
23
+ \ operation, please complete a tree.\n\n![example](./images/example.gif)\n\nProblem Statement\n--------\nThere are $N^2-1$\
24
+ \ tiles on an $N \\times N$ board.\nLet $(i, j)$ denote the coordinates of row $i$ $(0\\leq i \\leq N-1)$ from the top\
25
+ \ and column $j$ $(0\\leq j\\leq N-1)$ from the left.\nEach tile contains a figure with lines from its center towards\
26
+ \ one or more of four directions: up, down, left, and right.\nWe represent each tile using a bitmask with 1 for left,\
27
+ \ 2 for up, 4 for right, and 8 for down, as follows.\n\n<table>\n<tr align=\"center\">\n<td>\nTile\n</td>\n<td>\n<svg\
28
+ \ height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect fill=\"white\"\
29
+ \ height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"\
30
+ 0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"/>\n<line stroke=\"\
31
+ lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\"\
32
+ \ x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<rect fill=\"lightgray\" height=\"40\" width=\"40\" x=\"0\" y=\"0\"/>\n</svg>\n\
33
+ </td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n\
34
+ <rect fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"\
35
+ 0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"\
36
+ />\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\"\
37
+ \ stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"translate(0,0)\">\n<line stroke=\"#905020\"\
38
+ \ stroke-width=\"10\" x1=\"20\" x2=\"0\" y1=\"20\" y2=\"20\"/>\n<circle cx=\"20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n\
39
+ </g>\n</svg>\n</td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\"\
40
+ >\n<rect fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\"\
41
+ \ x1=\"0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"\
42
+ 40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\"\
43
+ \ stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"translate(0,0)\">\n<line stroke=\"#905020\"\
44
+ \ stroke-width=\"10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"0\"/>\n<circle cx=\"20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n\
45
+ </g>\n</svg>\n</td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\"\
46
+ >\n<rect fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\"\
47
+ \ x1=\"0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"\
48
+ 40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\"\
49
+ \ stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"translate(0,0)\">\n<line stroke=\"#905020\"\
50
+ \ stroke-width=\"10\" x1=\"20\" x2=\"0\" y1=\"20\" y2=\"20\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\"\
51
+ \ x2=\"20\" y1=\"20\" y2=\"0\"/>\n<circle cx=\"20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n<td>\n\
52
+ <svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect fill=\"\
53
+ white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\"\
54
+ \ y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"/>\n<line stroke=\"\
55
+ lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\"\
56
+ \ x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"translate(0,0)\">\n<line stroke=\"#905020\" stroke-width=\"\
57
+ 10\" x1=\"20\" x2=\"40\" y1=\"20\" y2=\"20\"/>\n<circle cx=\"20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n\
58
+ </td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n\
59
+ <rect fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"\
60
+ 0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"\
61
+ />\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\"\
62
+ \ stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"translate(0,0)\">\n<line stroke=\"#905020\"\
63
+ \ stroke-width=\"10\" x1=\"20\" x2=\"0\" y1=\"20\" y2=\"20\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\"\
64
+ \ x2=\"40\" y1=\"20\" y2=\"20\"/>\n<circle cx=\"20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n<td>\n\
65
+ <svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect fill=\"\
66
+ white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\"\
67
+ \ y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"/>\n<line stroke=\"\
68
+ lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\"\
69
+ \ x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"translate(0,0)\">\n<line stroke=\"#905020\" stroke-width=\"\
70
+ 10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"0\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"40\" y1=\"\
71
+ 20\" y2=\"20\"/>\n<circle cx=\"20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n<td>\n<svg height=\"50\"\
72
+ \ id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect fill=\"white\" height=\"\
73
+ 50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"0\" y2=\"\
74
+ 0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"/>\n<line stroke=\"lightgray\"\
75
+ \ stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"40\"\
76
+ \ x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"translate(0,0)\">\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"\
77
+ 20\" x2=\"0\" y1=\"20\" y2=\"20\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"\
78
+ 0\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"40\" y1=\"20\" y2=\"20\"/>\n<circle cx=\"20\" cy=\"\
79
+ 20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"\
80
+ 50\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line\
81
+ \ stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"\
82
+ 1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\"\
83
+ \ y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"\
84
+ translate(0,0)\">\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"40\"/>\n<circle cx=\"\
85
+ 20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50\
86
+ \ 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"\
87
+ -5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\"\
88
+ \ stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"\
89
+ 40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g\
90
+ \ transform=\"translate(0,0)\">\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"0\" y1=\"20\" y2=\"20\"/>\n\
91
+ <line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"40\"/>\n<circle cx=\"20\" cy=\"20\" fill=\"\
92
+ #905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"\
93
+ http://www.w3.org/2000/svg\">\n<rect fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\"\
94
+ \ stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"\
95
+ 0\" y1=\"0\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line\
96
+ \ stroke=\"lightgray\" stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"translate(0,0)\">\n\
97
+ <line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"0\"/>\n<line stroke=\"#905020\" stroke-width=\"\
98
+ 10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"40\"/>\n<circle cx=\"20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n\
99
+ </td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n\
100
+ <rect fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"\
101
+ 0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"\
102
+ />\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\"\
103
+ \ stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"translate(0,0)\">\n<line stroke=\"#905020\"\
104
+ \ stroke-width=\"10\" x1=\"20\" x2=\"0\" y1=\"20\" y2=\"20\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\"\
105
+ \ x2=\"20\" y1=\"20\" y2=\"0\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"40\"\
106
+ />\n<circle cx=\"20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"\
107
+ -5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect fill=\"white\" height=\"50\" width=\"50\" x=\"\
108
+ -5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"\
109
+ lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"\
110
+ 0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"\
111
+ 40\"/>\n<g transform=\"translate(0,0)\">\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"40\" y1=\"20\" y2=\"\
112
+ 20\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"40\"/>\n<circle cx=\"20\" cy=\"\
113
+ 20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"\
114
+ 50\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line\
115
+ \ stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"\
116
+ 1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\"\
117
+ \ y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"\
118
+ translate(0,0)\">\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"0\" y1=\"20\" y2=\"20\"/>\n<line stroke=\"\
119
+ #905020\" stroke-width=\"10\" x1=\"20\" x2=\"40\" y1=\"20\" y2=\"20\"/>\n<line stroke=\"#905020\" stroke-width=\"10\"\
120
+ \ x1=\"20\" x2=\"20\" y1=\"20\" y2=\"40\"/>\n<circle cx=\"20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n\
121
+ <td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect\
122
+ \ fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"-5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\"\
123
+ \ x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"/>\n\
124
+ <line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"\
125
+ 1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g transform=\"translate(0,0)\">\n<line stroke=\"#905020\" stroke-width=\"\
126
+ 10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"0\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"40\" y1=\"\
127
+ 20\" y2=\"20\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"40\"/>\n<circle cx=\"\
128
+ 20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n<td>\n<svg height=\"50\" id=\"vis\" viewBox=\"-5 -5 50\
129
+ \ 50\" width=\"50\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect fill=\"white\" height=\"50\" width=\"50\" x=\"-5\" y=\"\
130
+ -5\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"40\" y1=\"0\" y2=\"0\"/>\n<line stroke=\"lightgray\"\
131
+ \ stroke-width=\"1\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"0\" x2=\"\
132
+ 40\" y1=\"40\" y2=\"40\"/>\n<line stroke=\"lightgray\" stroke-width=\"1\" x1=\"40\" x2=\"40\" y1=\"0\" y2=\"40\"/>\n<g\
133
+ \ transform=\"translate(0,0)\">\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"0\" y1=\"20\" y2=\"20\"/>\n\
134
+ <line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"20\" y1=\"20\" y2=\"0\"/>\n<line stroke=\"#905020\" stroke-width=\"\
135
+ 10\" x1=\"20\" x2=\"40\" y1=\"20\" y2=\"20\"/>\n<line stroke=\"#905020\" stroke-width=\"10\" x1=\"20\" x2=\"20\" y1=\"\
136
+ 20\" y2=\"40\"/>\n<circle cx=\"20\" cy=\"20\" fill=\"#905020\" r=\"5\"/>\n</g>\n</svg>\n</td>\n</tr>\n<tr align=\"center\"\
137
+ >\n<td>\nBinary\n</td>\n<td>\n0000\n</td>\n<td>\n0001\n</td>\n<td>\n0010\n</td>\n<td>\n0011\n</td>\n<td>\n0100\n</td>\n\
138
+ <td>\n0101\n</td>\n<td>\n0110\n</td>\n<td>\n0111\n</td>\n<td>\n1000\n</td>\n<td>\n1001\n</td>\n<td>\n1010\n</td>\n<td>\n\
139
+ 1011\n</td>\n<td>\n1100\n</td>\n<td>\n1101\n</td>\n<td>\n1110\n</td>\n<td>\n1111\n</td>\n</tr>\n<tr align=\"center\">\n\
140
+ <td>\nHex\n</td>\n<td>\n0\n</td>\n<td>\n1\n</td>\n<td>\n2\n</td>\n<td>\n3\n</td>\n<td>\n4\n</td>\n<td>\n5\n</td>\n<td>\n\
141
+ 6\n</td>\n<td>\n7\n</td>\n<td>\n8\n</td>\n<td>\n9\n</td>\n<td>\na\n</td>\n<td>\nb\n</td>\n<td>\nc\n</td>\n<td>\nd\n</td>\n\
142
+ <td>\ne\n</td>\n<td>\nf\n</td>\n</tr>\n</table>\n\nThe number 0 represents an empty square, and there is exactly one empty\
143
+ \ square.\nWith a single operation, you can slide one of the tiles adjacent to the empty square in the four directions\
144
+ \ to the location of the empty square. After the move, the square from which the tile was moved becomes an empty square.\n\
145
+ You can repeat the sliding operation at most $T=2\\times N^3$ times.\n\nAfter finishing the operations, consider a graph\
146
+ \ with $N^2-1$ squares other than the empty square as vertices and the following edges.\n\n- For each $(i, j)$ $(0\\leq\
147
+ \ i\\leq N-2, 0\\leq j\\leq N-1)$, if $(i,j)$ is a tile with a downward line and $(i+1,j)$ is a tile with an upward line,\
148
+ \ then construct an edge between $(i,j)$ and $(i+1,j)$.\n- For each $(i, j)$ $(0\\leq i\\leq N-1, 0\\leq j\\leq N-2)$,\
149
+ \ if $(i,j)$ is a tile with a rightward line and $(i,j+1)$ is a tile with a leftward line, then construct an edge between\
150
+ \ $(i,j)$ and $(i,j+1)$.\n\nYour task is to find a short sequence of operations such that the size of the largest tree\
151
+ \ in this graph, i.e., the number of vertices of the largest connected component without cycles, is as large as possible.\n\
152
+ It is guaranteed that within $T$ operations you can construct a tree of size $N^2-1$ with the empty square in $(N-1,N-1)$.\n\
153
+ Note that the final position of the empty square is arbitrary and you do not have to move it to $(N-1,N-1)$.\n\nScoring\n\
154
+ --------\nLet $K$ be the number of operations and $S$ be the size of the largest tree painted on the board after applying\
155
+ \ the sequence of operations.\nThen, you will get the following score.\n\n- If $S<N^2-1$, $\\mathrm{round}\\left(500000\\\
156
+ times \\frac{S}{N^2-1}\\right)$\n- If $S=N^2-1$, $\\mathrm{round}\\left(500000\\times (2-\\frac{K}{T})\\right)$\n\nIf\
157
+ \ the number of operations exceeds $T$ or you perform an illegal operation to move a non-existent tile to the empty square,\
158
+ \ it will be judged as <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"不正解\">WA</span>\
159
+ \ .\n\n#### Number of test cases\n- Provisional test: 50\n- System test: 3000. We will publish <a href=\"https://img.atcoder.jp/ahc011/seeds.txt\"\
160
+ >seeds.txt</a> (sha256=041256f962c6ba1a60294ad7a575684d6e401163cba316cf978f2e66a4f7b0e3) after the contest is over.\n\
161
+ - Both provisional and system tests contain the same number of inputs for each $N=6,7,8,9,10$.\n\nThe score of a submission\
162
+ \ is the total scores for each test case.\nIn the provisional test, if your submission produces illegal output or exceeds\
163
+ \ the time limit for some test cases, the submission itself will be judged as <span class='label label-warning' data-toggle='tooltip'\
164
+ \ data-placement='top' title=\"Wrong Answer\">WA</span> or <span class='label label-warning' data-toggle='tooltip' data-placement='top'\
165
+ \ title=\"Time Limit Exceeded\">TLE</span> , and the score of the submission will be zero.\nIn the system test, if your\
166
+ \ submission produces illegal output or exceeds the time limit for some test cases, only the score for those test cases\
167
+ \ will be zero.\n\n#### About execution time\nExecution time may vary slightly from run to run.\nIn addition, since system\
168
+ \ tests simultaneously perform a large number of executions, it has been observed that execution time increases by several\
169
+ \ percent compared to provisional tests.\nFor these reasons, submissions that are very close to the time limit may result\
170
+ \ in <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Time Limit Exceeded\">TLE</span>\
171
+ \ in the s\nystem test.\nPlease measure the execution time in your program to terminate the process, or have enough margin\
172
+ \ in the execution time.\n\n\nInput\n--------\nInput is given from Standard Input in the following format:\n\n~~~\n$N$\
173
+ \ $T$\n$t_{0,0}$ $\\cdots$ $t_{0,N-1}$\n$\\vdots$\n$t_{N-1,0}$ $\\cdots$ $t_{N-1,N-1}$\n~~~\n\n$N$ is an integer representing\
174
+ \ the height and width of the board, satisfying $6\\leq N\\leq 10$.\nIn all test cases, $T=2\\times N^3$.\n$t_{i,0}$ $\\\
175
+ cdots$ $t_{i,N-1}$ is a string of length $N$.\nThe $j$-th character $t_{i,j}$ is `0`-`9` or `a`-`f` which is the hexadecimal\
176
+ \ representation of the figure contained in the tile $(i,j)$.\n\nOutput\n--------\n\nBy representing each operation of\
177
+ \ sliding the upward, downward, leftward, or rightward adjacent tile into the empty square by a single character `U`,\
178
+ \ `D`, `L` or `R`, respectively, output the sequence of $K$ operations as a string of length $K$ in one line to Standard\
179
+ \ Output.\n\n<a href=\"https://img.atcoder.jp/ahc011/df8bb452a2.html?lang=en&seed=0&output=RRRDLUULDDDDLUUUR\">Show example</a>\n\
180
+ \n\nInput Generation\n--------\n<details>\n\n#### Generation of $N$ and $T$\nWe generate $N$ as the remainder of the seed\
181
+ \ value divided by 5 + 6.\nHence, you can generate inputs with a specific $N$ value by adjusting the seed value.\nWe set\
182
+ \ $T=2\\times N^3$.\n\n#### Generation of $t_{i,j}$\nLet $[k]=\\\\{0,1,\\cdots,k-1\\\\}$.\nWe randomly generate a spanning\
183
+ \ tree $(V,F)$ with vertices $V=[N]\\times [N]\\setminus \\\\{(N-1,N-1)\\\\}$ as follows.\n\n1. First, we randomly shuffle\
184
+ \ edges $\\\\{\\\\{(i,j),(i+1,j)\\\\}\\mid (i,j)\\in [N-1]\\times [N]\\setminus \\\\{(N-2,N-1)\\\\}\\\\}\\cup\\\\{\\\\\
185
+ {(i,j),(i,j+1)\\\\}\\mid (i,j)\\in [N]\\times [N-1]\\setminus \\\\{(N-1,N-2)\\\\}\\\\}$ and obtain an ordered edge list\
186
+ \ $e_0, e_1, \\cdots$.\n2. Starting from $F=\\emptyset$, for each $e_k=\\\\{(i,j),(i',j')\\\\}$, we insert $e_k$ into\
187
+ \ $F$ if $(i,j)$ and $(i',j')$ are not connected in $(V,F)$.\n\nFrom the obtained spanning tree, we construct tiles on\
188
+ \ which a tree of size $N^2-1$ is drawn, as follows.\n\n1. For each $(i,j)$, if $\\\\{(i,j),(i+1,j)\\\\}\\in F$, then\
189
+ \ draw a downward line on tile $(i, j)$ and an upward line on tile $(i+1,j)$.\n2. For each $(i,j)$, if $\\\\{(i,j),(i,j+1)\\\
190
+ \\}\\in F$, then draw a rightward line on tile $(i, j)$ and a leftward line on tile $(i,j+1)$.\n\nFinally, starting from\
191
+ \ the constructed tile layout, randomly perform $T=2\\times N^3$ sliding operations, and let $t$ be the tile layout after\
192
+ \ the operations.\nHere, the $k (\\geq 2)$-th operation is chosen uniformly at random from at most three directions excluding\
193
+ \ the direction that reverts the $(k-1)$-th operation.\n\n</details>\n\nTools (Input generator and visualizer)\n--------\n\
194
+ - <a href=\"https://img.atcoder.jp/ahc011/df8bb452a2.html?lang=en\">Web version</a>: This is more powerful than the local\
195
+ \ version and can display animations.\n- <a href=\"https://img.atcoder.jp/ahc011/df8bb452a2.zip\">Local version</a>: You\
196
+ \ need a compilation environment of <a href=\"https://www.rust-lang.org/\">Rust language</a>.\n - <a href=\"https://img.atcoder.jp/ahc011/df8bb452a2_windows.zip\"\
197
+ >Pre-compiled binary for Windows</a>: If you are not familiar with the Rust language environment, please use this instead.\n\
198
+ \n<font color=\"red\">\nYou are allowed to share output images (PNG) of the provided visualizer for seed=0 on twitter\
199
+ \ during the contest.\nNote that sharing in video format is prohibited.\n</font>\nYou have to use the specified hashtag\
200
+ \ and public account.\nYou can only share visualization results and scores for seed=0.\nDo not share GIFs, output itself,\
201
+ \ scores for other seeds or mention solutions or discussions.\n<font color=\"red\">\n(Added) The visualizer has a feature\
202
+ \ to change the value of N, but sharing visualization results for changed inputs is also prohibited.\n</font>\n\n<a href=\"\
203
+ https://twitter.com/search?q=%23AHC011%20%23visualizer&src=typed_query&f=live\">List of shared images</a>\n\n{sample example}\n\
204
+ \n\n Problem constraints:\n time_limit=3.0 memory_limit=1073741824\n"
205
+ evaluator:
206
+ timeout: 10000
207
+ cascade_evaluation: false
208
+
benchmarks/ale_bench/ale-bench-lite-problems/ahc011/evaluator.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+ from pathlib import Path
3
+ from ale_bench.result import CaseResult, JudgeResult, Result
4
+ from ale_bench_eval.safe_ale_session import start_ale_bench_session
5
+ import logging
6
+ import sys
7
+ logger = logging.getLogger(__name__ + "_" + "ALE_BENCH_EVALUATOR")
8
+
9
+ def result_feedback(result: Result) -> CaseResult:
10
+ if result.overall_judge_result == JudgeResult.ACCEPTED:
11
+ return result.case_results[0]
12
+ else:
13
+ selected_case_idx = 0
14
+ for idx, case_result in enumerate(result.case_results):
15
+ if case_result.judge_result == result.overall_judge_result:
16
+ selected_case_idx = idx
17
+ break
18
+ return result.case_results[selected_case_idx]
19
+
20
+ def evaluate(program_path):
21
+ problem_id = "ahc011"
22
+ logger.info(f"Evaluating program {program_path} for problem {problem_id} in ale bench evaluator")
23
+ try:
24
+ session = None
25
+ logger.info("Starting ALE-Bench session")
26
+ session = start_ale_bench_session(
27
+ problem_id=problem_id,
28
+ lite_version=True,
29
+ num_workers=13,
30
+ )
31
+ logger.info("ALE-Bench session started")
32
+ if not session:
33
+ raise RuntimeError("Failed to start or restart the session.")
34
+ optim_factor = 1 if session.problem.metadata.score_type == "maximize" else -1
35
+ code = Path(program_path).read_text().replace("# EVOLVE-BLOCK-START", "").replace("# EVOLVE-BLOCK-END", "").strip()
36
+ logger.info("Code extracted")
37
+ num_public_cases = 50
38
+ cases = session.case_gen(list(range(num_public_cases)))
39
+ public_result = session.case_eval(
40
+ cases, code, code_language="cpp20", skip_local_visualization=True
41
+ )
42
+ logger.info("Public evaluation completed")
43
+ extracted_case = result_feedback(public_result)
44
+ logger.info("Result feedback completed")
45
+ logger.info("ALE-Bench session closed")
46
+ combined_score = public_result.overall_absolute_score * optim_factor / num_public_cases
47
+ if public_result.overall_judge_result != JudgeResult.ACCEPTED and optim_factor == -1:
48
+ combined_score = -sys.maxsize - 1
49
+ session.close()
50
+ return {
51
+ "judge_result": public_result.overall_judge_result.value,
52
+ "overall_score": public_result.overall_absolute_score,
53
+ "max_execution_time_sec": max([case_result.execution_time for case_result in public_result.case_results]),
54
+ "max_memory_usage_mib": max([case_result.memory_usage for case_result in public_result.case_results]) // 1024 // 1024,
55
+ "standard_error": extracted_case.error_str,
56
+ "message": extracted_case.message,
57
+ "combined_score": combined_score,
58
+ }
59
+ except Exception as e:
60
+ logger.error(f"Evaluation failed completely: {str(e)}")
61
+ logger.error(traceback.format_exc())
62
+ return {
63
+ "overall_score": 0.0,
64
+ "error": str(e),
65
+ }
benchmarks/ale_bench/ale-bench-lite-problems/ahc011/initial_program.cpp ADDED
@@ -0,0 +1,607 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include <string>
5
+ #include <array>
6
+ #include <algorithm>
7
+ #include <unordered_map>
8
+ #include <map> // For A* visited set
9
+ #include <iomanip>
10
+ #include <chrono>
11
+ #include <functional> // For std::hash
12
+ #include <cmath> // For std::round
13
+ #include <random> // For std::mt19937
14
+ #include <numeric> // For std::iota
15
+ #include <queue> // For A* search (priority_queue)
16
+
17
+ // Constants for tile connections
18
+ const int LEFT_MASK = 1;
19
+ const int UP_MASK = 2;
20
+ const int RIGHT_MASK = 4;
21
+ const int DOWN_MASK = 8;
22
+
23
+ // Max N value, actual N read from input
24
+ const int N_MAX_CONST = 10;
25
+ int N_actual; // Actual N for the current test case
26
+ int T_param; // Actual T for the current test case
27
+
28
+ const int DR_TILE_RELATIVE_TO_EMPTY[] = {-1, 1, 0, 0};
29
+ const int DC_TILE_RELATIVE_TO_EMPTY[] = {0, 0, -1, 1};
30
+ const char MOVE_CHARS[] = {'U', 'D', 'L', 'R'};
31
+
32
+
33
+ std::mt19937 zobrist_rng_engine(123456789);
34
+ std::uniform_int_distribution<uint64_t> distrib_uint64;
35
+ uint64_t zobrist_tile_keys[N_MAX_CONST][N_MAX_CONST][16];
36
+
37
+
38
+ void init_zobrist_keys() {
39
+ for (int i = 0; i < N_actual; ++i) {
40
+ for (int j = 0; j < N_actual; ++j) {
41
+ for (int k = 0; k < 16; ++k) {
42
+ zobrist_tile_keys[i][j][k] = distrib_uint64(zobrist_rng_engine);
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ int hex_char_to_int(char c) {
49
+ if (c >= '0' && c <= '9') return c - '0';
50
+ return c - 'a' + 10;
51
+ }
52
+
53
+
54
+ struct Board {
55
+ std::array<std::array<char, N_MAX_CONST>, N_MAX_CONST> tiles;
56
+ int empty_r, empty_c;
57
+ uint64_t zobrist_hash_value;
58
+
59
+ Board() : empty_r(0), empty_c(0), zobrist_hash_value(0) {}
60
+
61
+ void calculate_initial_hash() {
62
+ zobrist_hash_value = 0;
63
+ for (int i = 0; i < N_actual; ++i) {
64
+ for (int j = 0; j < N_actual; ++j) {
65
+ zobrist_hash_value ^= zobrist_tile_keys[i][j][hex_char_to_int(tiles[i][j])];
66
+ }
67
+ }
68
+ }
69
+
70
+ void update_hash_after_move(int pos_tile_becomes_empty_r, int pos_tile_becomes_empty_c,
71
+ int pos_empty_gets_tile_r, int pos_empty_gets_tile_c) {
72
+ int moved_tile_val_int = hex_char_to_int(tiles[pos_empty_gets_tile_r][pos_empty_gets_tile_c]);
73
+
74
+ zobrist_hash_value ^= zobrist_tile_keys[pos_tile_becomes_empty_r][pos_tile_becomes_empty_c][moved_tile_val_int];
75
+ zobrist_hash_value ^= zobrist_tile_keys[pos_empty_gets_tile_r][pos_empty_gets_tile_c][0];
76
+
77
+ zobrist_hash_value ^= zobrist_tile_keys[pos_tile_becomes_empty_r][pos_tile_becomes_empty_c][0];
78
+ zobrist_hash_value ^= zobrist_tile_keys[pos_empty_gets_tile_r][pos_empty_gets_tile_c][moved_tile_val_int];
79
+ }
80
+
81
+ bool apply_move_char(char move_char) {
82
+ int move_dir_idx = -1;
83
+ for(int i=0; i<4; ++i) if(MOVE_CHARS[i] == move_char) move_dir_idx = i;
84
+
85
+ if(move_dir_idx == -1) return false;
86
+
87
+ int tile_to_move_r = empty_r + DR_TILE_RELATIVE_TO_EMPTY[move_dir_idx];
88
+ int tile_to_move_c = empty_c + DC_TILE_RELATIVE_TO_EMPTY[move_dir_idx];
89
+
90
+ if (tile_to_move_r < 0 || tile_to_move_r >= N_actual || tile_to_move_c < 0 || tile_to_move_c >= N_actual) {
91
+ return false;
92
+ }
93
+
94
+ char moved_tile_hex_val = tiles[tile_to_move_r][tile_to_move_c];
95
+ tiles[empty_r][empty_c] = moved_tile_hex_val;
96
+ tiles[tile_to_move_r][tile_to_move_c] = '0';
97
+
98
+ update_hash_after_move(tile_to_move_r, tile_to_move_c, empty_r, empty_c);
99
+
100
+ empty_r = tile_to_move_r;
101
+ empty_c = tile_to_move_c;
102
+ return true;
103
+ }
104
+ };
105
+
106
+
107
+ struct ScoreComponents {
108
+ int max_tree_size;
109
+ int num_components;
110
+ };
111
+ std::unordered_map<uint64_t, ScoreComponents> s_value_cache_by_hash;
112
+ const size_t MAX_SCORE_CACHE_SIZE_CONST = 2000000;
113
+
114
+ struct DSU {
115
+ std::vector<int> parent;
116
+ std::vector<int> nodes_in_set;
117
+ std::vector<int> edges_in_set;
118
+ int N_sq_total_cells;
119
+
120
+ DSU(int current_N) : N_sq_total_cells(current_N * current_N) {
121
+ parent.resize(N_sq_total_cells);
122
+ std::iota(parent.begin(), parent.end(), 0);
123
+ nodes_in_set.assign(N_sq_total_cells, 0);
124
+ edges_in_set.assign(N_sq_total_cells, 0);
125
+ }
126
+
127
+ int find(int i) {
128
+ if (parent[i] == i)
129
+ return i;
130
+ return parent[i] = find(parent[i]);
131
+ }
132
+
133
+ void unite(int i_idx, int j_idx) {
134
+ int root_i = find(i_idx);
135
+ int root_j = find(j_idx);
136
+
137
+ if (nodes_in_set[root_i] < nodes_in_set[root_j]) std::swap(root_i, root_j);
138
+
139
+ parent[root_j] = root_i;
140
+ nodes_in_set[root_i] += nodes_in_set[root_j];
141
+ edges_in_set[root_i] += edges_in_set[root_j];
142
+ }
143
+
144
+ void add_edge(int u_idx, int v_idx) {
145
+ int root_u = find(u_idx);
146
+ int root_v = find(v_idx);
147
+ if (root_u != root_v) {
148
+ unite(u_idx, v_idx);
149
+ edges_in_set[find(u_idx)]++;
150
+ } else {
151
+ edges_in_set[root_u]++;
152
+ }
153
+ }
154
+ };
155
+
156
+
157
+ ScoreComponents calculate_scores(const Board& board) {
158
+ auto it_cache = s_value_cache_by_hash.find(board.zobrist_hash_value);
159
+ if (it_cache != s_value_cache_by_hash.end()) {
160
+ return it_cache->second;
161
+ }
162
+
163
+ DSU dsu(N_actual);
164
+
165
+ for (int r = 0; r < N_actual; ++r) {
166
+ for (int c = 0; c < N_actual; ++c) {
167
+ int cell_idx = r * N_actual + c;
168
+ if (board.tiles[r][c] != '0') {
169
+ dsu.nodes_in_set[cell_idx] = 1;
170
+ } else {
171
+ dsu.nodes_in_set[cell_idx] = 0;
172
+ }
173
+ }
174
+ }
175
+
176
+ for (int r = 0; r < N_actual; ++r) {
177
+ for (int c = 0; c < N_actual - 1; ++c) {
178
+ int tile1_val = hex_char_to_int(board.tiles[r][c]);
179
+ int tile2_val = hex_char_to_int(board.tiles[r][c+1]);
180
+ if (tile1_val != 0 && tile2_val != 0) {
181
+ if ((tile1_val & RIGHT_MASK) && (tile2_val & LEFT_MASK)) {
182
+ dsu.add_edge(r * N_actual + c, r * N_actual + (c + 1));
183
+ }
184
+ }
185
+ }
186
+ }
187
+ for (int r = 0; r < N_actual - 1; ++r) {
188
+ for (int c = 0; c < N_actual; ++c) {
189
+ int tile1_val = hex_char_to_int(board.tiles[r][c]);
190
+ int tile2_val = hex_char_to_int(board.tiles[r+1][c]);
191
+ if (tile1_val != 0 && tile2_val != 0) {
192
+ if ((tile1_val & DOWN_MASK) && (tile2_val & UP_MASK)) {
193
+ dsu.add_edge(r * N_actual + c, (r + 1) * N_actual + c);
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ int max_tree_size = 0;
200
+ int total_num_components = 0;
201
+
202
+ for (int i = 0; i < dsu.N_sq_total_cells; ++i) {
203
+ if (dsu.parent[i] == i && dsu.nodes_in_set[i] > 0) {
204
+ total_num_components++;
205
+ if (dsu.edges_in_set[i] == dsu.nodes_in_set[i] - 1) {
206
+ if (dsu.nodes_in_set[i] > max_tree_size) {
207
+ max_tree_size = dsu.nodes_in_set[i];
208
+ }
209
+ }
210
+ }
211
+ }
212
+
213
+ ScoreComponents result = {max_tree_size, total_num_components};
214
+ if (s_value_cache_by_hash.size() < MAX_SCORE_CACHE_SIZE_CONST) {
215
+ s_value_cache_by_hash[board.zobrist_hash_value] = result;
216
+ }
217
+ return result;
218
+ }
219
+
220
+
221
+ int TARGET_EMPTY_R_GLOBAL_FOR_A_STAR, TARGET_EMPTY_C_GLOBAL_FOR_A_STAR; // Used by A* heuristic
222
+ bool A_STAR_PHASE_WAS_RUN = false; // Flag to adjust beam score empty penalty
223
+
224
+ double calculate_beam_score(const ScoreComponents& scores, int K_total, const Board& current_board_state) {
225
+ int S = scores.max_tree_size;
226
+
227
+ const double FULL_TREE_BASE_SCORE = 1e18;
228
+ if (S == N_actual * N_actual - 1) {
229
+ return FULL_TREE_BASE_SCORE + (double)(T_param * 2 - K_total);
230
+ }
231
+
232
+ double W_S = 1e9;
233
+ double W_NC = W_S * 0.8; // Make W_NC very strong, almost as much as increasing S by 1.
234
+ double W_K = 1.0;
235
+ double W_empty_dist_penalty_main;
236
+
237
+ if (A_STAR_PHASE_WAS_RUN) { // A* moved empty to target initially
238
+ W_empty_dist_penalty_main = W_K * 0.5; // Very low penalty, allow free movement
239
+ } else { // Empty started at target, or A* failed (should not happen)
240
+ W_empty_dist_penalty_main = W_K * 10.0; // Moderate penalty
241
+ }
242
+
243
+ double score_val = (double)S * W_S;
244
+ if (scores.num_components > 1) {
245
+ score_val -= (double)(scores.num_components - 1) * W_NC;
246
+ } else if (scores.num_components == 0 && N_actual * N_actual - 1 > 0) {
247
+ score_val -= (double)(N_actual * N_actual -1) * W_NC;
248
+ }
249
+
250
+ // Bonus for being very close to a full tree and connected
251
+ if (S >= (N_actual * N_actual - 1) - 2 && scores.num_components == 1 && S < N_actual * N_actual - 1) {
252
+ score_val += W_S * 0.5; // Significant bonus to encourage the last step
253
+ }
254
+
255
+ score_val -= (double)K_total * W_K;
256
+
257
+ // Penalty for empty square relative to (N-1,N-1)
258
+ int dist_empty_to_corner = std::abs(current_board_state.empty_r - (N_actual - 1)) +
259
+ std::abs(current_board_state.empty_c - (N_actual - 1));
260
+ score_val -= dist_empty_to_corner * W_empty_dist_penalty_main;
261
+
262
+ return score_val;
263
+ }
264
+
265
+ double calculate_actual_score(int S, int K_total) {
266
+ if (N_actual * N_actual - 1 == 0) return 0;
267
+ if (S == N_actual * N_actual - 1) {
268
+ if (K_total > T_param) return 0;
269
+ return std::round(500000.0 * (2.0 - (double)K_total / T_param));
270
+ } else {
271
+ return std::round(500000.0 * (double)S / (N_actual * N_actual - 1.0));
272
+ }
273
+ }
274
+
275
+ struct BeamHistoryEntry {
276
+ int parent_history_idx;
277
+ char move_char_taken;
278
+ };
279
+ std::vector<BeamHistoryEntry> beam_history_storage;
280
+ const size_t MAX_BEAM_HISTORY_STORAGE_SIZE_CONST = 3000000;
281
+
282
+ struct BeamState {
283
+ Board board;
284
+ double beam_score_val;
285
+ int k_beam_moves;
286
+ int history_idx;
287
+ int prev_move_direction_idx;
288
+
289
+ bool operator<(const BeamState& other) const {
290
+ return beam_score_val > other.beam_score_val;
291
+ }
292
+ };
293
+
294
+ std::chrono::steady_clock::time_point T_START_CHRONO_MAIN;
295
+ const int TIME_LIMIT_MS_SLACK_CONST = 400; // Universal slack
296
+ long long TIME_LIMIT_MS_EFFECTIVE_MAIN;
297
+
298
+
299
+ std::mt19937 rng_stochastic_selection_main;
300
+ std::unordered_map<uint64_t, int> min_K_to_reach_by_hash_main;
301
+ const size_t MAX_MIN_K_CACHE_SIZE_CONST = 2000000;
302
+
303
+
304
+ struct AStarEmptyState {
305
+ int r, c;
306
+ int g_cost;
307
+ std::string path;
308
+
309
+ bool operator>(const AStarEmptyState& other) const {
310
+ int h_cost_this = std::abs(r - TARGET_EMPTY_R_GLOBAL_FOR_A_STAR) + std::abs(c - TARGET_EMPTY_C_GLOBAL_FOR_A_STAR);
311
+ int h_cost_other = std::abs(other.r - TARGET_EMPTY_R_GLOBAL_FOR_A_STAR) + std::abs(other.c - TARGET_EMPTY_C_GLOBAL_FOR_A_STAR);
312
+ if (g_cost + h_cost_this != other.g_cost + h_cost_other) {
313
+ return g_cost + h_cost_this > other.g_cost + h_cost_other;
314
+ }
315
+ return g_cost > other.g_cost;
316
+ }
317
+ };
318
+
319
+ std::string find_path_for_empty(const Board& initial_board_state_for_A_star, int target_r, int target_c) {
320
+ TARGET_EMPTY_R_GLOBAL_FOR_A_STAR = target_r;
321
+ TARGET_EMPTY_C_GLOBAL_FOR_A_STAR = target_c;
322
+
323
+ std::priority_queue<AStarEmptyState, std::vector<AStarEmptyState>, std::greater<AStarEmptyState>> pq;
324
+ std::vector<std::vector<int>> min_g_cost_grid(N_actual, std::vector<int>(N_actual, T_param + 1));
325
+
326
+ pq.push({initial_board_state_for_A_star.empty_r, initial_board_state_for_A_star.empty_c, 0, ""});
327
+ min_g_cost_grid[initial_board_state_for_A_star.empty_r][initial_board_state_for_A_star.empty_c] = 0;
328
+
329
+ int A_star_max_depth = N_actual * N_actual * 2; // Allow more depth just in case
330
+
331
+ while(!pq.empty()){
332
+ AStarEmptyState current = pq.top();
333
+ pq.pop();
334
+
335
+ if (current.g_cost > min_g_cost_grid[current.r][current.c]) {
336
+ continue;
337
+ }
338
+
339
+ if (current.r == target_r && current.c == target_c) {
340
+ return current.path;
341
+ }
342
+
343
+ if (current.g_cost >= A_star_max_depth) continue;
344
+
345
+ for (int move_idx = 0; move_idx < 4; ++move_idx) {
346
+ int tile_that_moves_r = current.r + DR_TILE_RELATIVE_TO_EMPTY[move_idx];
347
+ int tile_that_moves_c = current.c + DC_TILE_RELATIVE_TO_EMPTY[move_idx];
348
+
349
+ if (tile_that_moves_r < 0 || tile_that_moves_r >= N_actual || tile_that_moves_c < 0 || tile_that_moves_c >= N_actual) {
350
+ continue;
351
+ }
352
+
353
+ int next_empty_r = tile_that_moves_r;
354
+ int next_empty_c = tile_that_moves_c;
355
+
356
+ int next_g_cost = current.g_cost + 1;
357
+
358
+ if (min_g_cost_grid[next_empty_r][next_empty_c] <= next_g_cost) {
359
+ continue;
360
+ }
361
+ min_g_cost_grid[next_empty_r][next_empty_c] = next_g_cost;
362
+ pq.push({next_empty_r, next_empty_c, next_g_cost, current.path + MOVE_CHARS[move_idx]});
363
+ }
364
+ }
365
+ return "";
366
+ }
367
+
368
+ std::string reconstruct_beam_path(int final_history_idx) {
369
+ std::string path_str = "";
370
+ int current_trace_hist_idx = final_history_idx;
371
+ while(current_trace_hist_idx > 0 &&
372
+ static_cast<size_t>(current_trace_hist_idx) < beam_history_storage.size() &&
373
+ beam_history_storage[current_trace_hist_idx].parent_history_idx != -1) {
374
+ path_str += beam_history_storage[current_trace_hist_idx].move_char_taken;
375
+ current_trace_hist_idx = beam_history_storage[current_trace_hist_idx].parent_history_idx;
376
+ }
377
+ std::reverse(path_str.begin(), path_str.end());
378
+ return path_str;
379
+ }
380
+
381
+
382
+ int main(int /*argc*/, char** /*argv*/) {
383
+ std::ios_base::sync_with_stdio(false);
384
+ std::cin.tie(NULL);
385
+
386
+ unsigned int random_seed_stochastic = std::chrono::steady_clock::now().time_since_epoch().count();
387
+ rng_stochastic_selection_main.seed(random_seed_stochastic);
388
+
389
+ T_START_CHRONO_MAIN = std::chrono::steady_clock::now();
390
+
391
+ std::cin >> N_actual >> T_param;
392
+
393
+ init_zobrist_keys();
394
+
395
+ Board current_board_obj;
396
+ for (int i = 0; i < N_actual; ++i) {
397
+ std::string row_str;
398
+ std::cin >> row_str;
399
+ for (int j = 0; j < N_actual; ++j) {
400
+ current_board_obj.tiles[i][j] = row_str[j];
401
+ if (current_board_obj.tiles[i][j] == '0') {
402
+ current_board_obj.empty_r = i;
403
+ current_board_obj.empty_c = j;
404
+ }
405
+ }
406
+ }
407
+ current_board_obj.calculate_initial_hash();
408
+
409
+ std::string initial_empty_moves_path = "";
410
+ int target_empty_final_r = N_actual - 1;
411
+ int target_empty_final_c = N_actual - 1;
412
+
413
+ if (current_board_obj.empty_r != target_empty_final_r || current_board_obj.empty_c != target_empty_final_c) {
414
+ initial_empty_moves_path = find_path_for_empty(current_board_obj, target_empty_final_r, target_empty_final_c);
415
+ A_STAR_PHASE_WAS_RUN = !initial_empty_moves_path.empty();
416
+ }
417
+
418
+ for (char move_char : initial_empty_moves_path) {
419
+ current_board_obj.apply_move_char(move_char);
420
+ }
421
+ int K_initial_empty_moves = initial_empty_moves_path.length();
422
+
423
+ // Adaptive time limit after A*
424
+ auto time_after_astar = std::chrono::steady_clock::now();
425
+ long long elapsed_astar_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_after_astar - T_START_CHRONO_MAIN).count();
426
+ TIME_LIMIT_MS_EFFECTIVE_MAIN = 2950 - elapsed_astar_ms - TIME_LIMIT_MS_SLACK_CONST;
427
+
428
+
429
+ beam_history_storage.reserve(MAX_BEAM_HISTORY_STORAGE_SIZE_CONST);
430
+ s_value_cache_by_hash.reserve(MAX_SCORE_CACHE_SIZE_CONST);
431
+ min_K_to_reach_by_hash_main.reserve(MAX_MIN_K_CACHE_SIZE_CONST);
432
+
433
+ std::vector<BeamState> current_beam;
434
+
435
+ ScoreComponents initial_scores_for_beam = calculate_scores(current_board_obj);
436
+ double initial_beam_eval_score = calculate_beam_score(initial_scores_for_beam, K_initial_empty_moves, current_board_obj);
437
+
438
+ beam_history_storage.push_back({-1, ' '});
439
+ current_beam.push_back({current_board_obj, initial_beam_eval_score, 0, 0, -1});
440
+
441
+ double overall_best_actual_score = calculate_actual_score(initial_scores_for_beam.max_tree_size, K_initial_empty_moves);
442
+ std::string overall_best_path_str = initial_empty_moves_path;
443
+
444
+ min_K_to_reach_by_hash_main[current_board_obj.zobrist_hash_value] = K_initial_empty_moves;
445
+
446
+ int beam_width;
447
+ float elite_ratio = 0.2f; // Standard elite ratio
448
+ int stochastic_sample_pool_factor = 3;
449
+
450
+ if (N_actual <= 6) { beam_width = 1200;} // N=6 is small, can afford wider
451
+ else if (N_actual == 7) { beam_width = 1000;}
452
+ else if (N_actual == 8) { beam_width = 700;} // Reduced from 800 to save time slightly
453
+ else if (N_actual == 9) { beam_width = 400;} // Reduced from 500
454
+ else { beam_width = 250;} // N=10, reduced from 300
455
+
456
+ std::vector<BeamState> candidates_pool;
457
+ candidates_pool.reserve(beam_width * 4 + 10);
458
+
459
+ std::vector<BeamState> next_beam_states_temp;
460
+ next_beam_states_temp.reserve(beam_width + 10);
461
+
462
+ std::vector<int> stochastic_selection_indices;
463
+ stochastic_selection_indices.reserve(stochastic_sample_pool_factor * beam_width + 10);
464
+
465
+ int k_iter_count_beam = 0;
466
+
467
+ for (int k_beam_iter = 0; K_initial_empty_moves + k_beam_iter < T_param; ++k_beam_iter) {
468
+ k_iter_count_beam++;
469
+ if (k_iter_count_beam % 10 == 0) {
470
+ if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - T_START_CHRONO_MAIN).count() > TIME_LIMIT_MS_EFFECTIVE_MAIN + elapsed_astar_ms) {
471
+ // Compare against total time budget, not just remaining for beam.
472
+ // Total time used > total budget minus slack
473
+ if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - T_START_CHRONO_MAIN).count() > 2950 - TIME_LIMIT_MS_SLACK_CONST) {
474
+ break;
475
+ }
476
+ }
477
+ }
478
+ if (beam_history_storage.size() >= MAX_BEAM_HISTORY_STORAGE_SIZE_CONST - ( (size_t)beam_width * 4 + 100) ) {
479
+ break;
480
+ }
481
+
482
+ candidates_pool.clear();
483
+
484
+ for (const auto& current_state_in_beam : current_beam) {
485
+ Board temp_board_for_moves = current_state_in_beam.board;
486
+
487
+ int parent_k_beam = current_state_in_beam.k_beam_moves;
488
+ int parent_history_idx = current_state_in_beam.history_idx;
489
+ int prev_m_dir_idx = current_state_in_beam.prev_move_direction_idx;
490
+
491
+ for (int move_dir_idx = 0; move_dir_idx < 4; ++move_dir_idx) {
492
+ if (prev_m_dir_idx != -1) {
493
+ if ((prev_m_dir_idx ^ 1) == move_dir_idx) { // Check for U/D or L/R reversal using XOR trick
494
+ continue;
495
+ }
496
+ }
497
+
498
+ char current_move_char = MOVE_CHARS[move_dir_idx];
499
+ int original_empty_r = temp_board_for_moves.empty_r;
500
+ int original_empty_c = temp_board_for_moves.empty_c;
501
+ uint64_t original_hash = temp_board_for_moves.zobrist_hash_value;
502
+
503
+ int tile_to_move_r = original_empty_r + DR_TILE_RELATIVE_TO_EMPTY[move_dir_idx];
504
+ int tile_to_move_c = original_empty_c + DC_TILE_RELATIVE_TO_EMPTY[move_dir_idx];
505
+
506
+ if (tile_to_move_r < 0 || tile_to_move_r >= N_actual || tile_to_move_c < 0 || tile_to_move_c >= N_actual) {
507
+ continue;
508
+ }
509
+
510
+ char moved_tile_hex_val = temp_board_for_moves.tiles[tile_to_move_r][tile_to_move_c];
511
+ temp_board_for_moves.tiles[original_empty_r][original_empty_c] = moved_tile_hex_val;
512
+ temp_board_for_moves.tiles[tile_to_move_r][tile_to_move_c] = '0';
513
+ temp_board_for_moves.empty_r = tile_to_move_r;
514
+ temp_board_for_moves.empty_c = tile_to_move_c;
515
+ temp_board_for_moves.update_hash_after_move(tile_to_move_r, tile_to_move_c, original_empty_r, original_empty_c);
516
+
517
+ int next_k_beam = parent_k_beam + 1;
518
+ int next_K_total = K_initial_empty_moves + next_k_beam;
519
+
520
+ bool already_reached_better = false;
521
+ auto it_map = min_K_to_reach_by_hash_main.find(temp_board_for_moves.zobrist_hash_value);
522
+ if (it_map != min_K_to_reach_by_hash_main.end()) {
523
+ if (it_map->second <= next_K_total) {
524
+ already_reached_better = true;
525
+ } else {
526
+ it_map->second = next_K_total;
527
+ }
528
+ } else {
529
+ if (min_K_to_reach_by_hash_main.size() < MAX_MIN_K_CACHE_SIZE_CONST) {
530
+ min_K_to_reach_by_hash_main[temp_board_for_moves.zobrist_hash_value] = next_K_total;
531
+ }
532
+ }
533
+
534
+ if(already_reached_better) {
535
+ temp_board_for_moves.tiles[tile_to_move_r][tile_to_move_c] = moved_tile_hex_val;
536
+ temp_board_for_moves.tiles[original_empty_r][original_empty_c] = '0';
537
+ temp_board_for_moves.empty_r = original_empty_r;
538
+ temp_board_for_moves.empty_c = original_empty_c;
539
+ temp_board_for_moves.zobrist_hash_value = original_hash;
540
+ continue;
541
+ }
542
+
543
+ ScoreComponents next_scores = calculate_scores(temp_board_for_moves);
544
+ double next_beam_eval_score = calculate_beam_score(next_scores, next_K_total, temp_board_for_moves);
545
+
546
+ beam_history_storage.push_back({parent_history_idx, current_move_char});
547
+ int new_history_idx = beam_history_storage.size() - 1;
548
+
549
+ candidates_pool.push_back({temp_board_for_moves, next_beam_eval_score, next_k_beam, new_history_idx, move_dir_idx});
550
+
551
+ double current_actual_score_val = calculate_actual_score(next_scores.max_tree_size, next_K_total);
552
+ if (current_actual_score_val > overall_best_actual_score) {
553
+ overall_best_actual_score = current_actual_score_val;
554
+ overall_best_path_str = initial_empty_moves_path + reconstruct_beam_path(new_history_idx);
555
+ } else if (current_actual_score_val == overall_best_actual_score) {
556
+ // Prefer shorter paths for same score
557
+ if ((initial_empty_moves_path + reconstruct_beam_path(new_history_idx)).length() < overall_best_path_str.length()){
558
+ overall_best_path_str = initial_empty_moves_path + reconstruct_beam_path(new_history_idx);
559
+ }
560
+ }
561
+
562
+ temp_board_for_moves.tiles[tile_to_move_r][tile_to_move_c] = moved_tile_hex_val;
563
+ temp_board_for_moves.tiles[original_empty_r][original_empty_c] = '0';
564
+ temp_board_for_moves.empty_r = original_empty_r;
565
+ temp_board_for_moves.empty_c = original_empty_c;
566
+ temp_board_for_moves.zobrist_hash_value = original_hash;
567
+ }
568
+ }
569
+
570
+ if (candidates_pool.empty()) break;
571
+
572
+ std::sort(candidates_pool.begin(), candidates_pool.end());
573
+
574
+ next_beam_states_temp.clear();
575
+ int num_elites = std::min(static_cast<int>(candidates_pool.size()), static_cast<int>(beam_width * elite_ratio));
576
+ num_elites = std::max(0, num_elites);
577
+
578
+ for(int i=0; i < num_elites && i < static_cast<int>(candidates_pool.size()); ++i) {
579
+ next_beam_states_temp.push_back(candidates_pool[i]);
580
+ }
581
+
582
+ if (next_beam_states_temp.size() < static_cast<size_t>(beam_width) && candidates_pool.size() > static_cast<size_t>(num_elites)) {
583
+ stochastic_selection_indices.clear();
584
+ int pool_start_idx = num_elites;
585
+ int pool_end_idx = std::min(static_cast<int>(candidates_pool.size()), num_elites + stochastic_sample_pool_factor * beam_width);
586
+
587
+ for(int i = pool_start_idx; i < pool_end_idx; ++i) {
588
+ stochastic_selection_indices.push_back(i);
589
+ }
590
+ if (!stochastic_selection_indices.empty()){
591
+ std::shuffle(stochastic_selection_indices.begin(), stochastic_selection_indices.end(), rng_stochastic_selection_main);
592
+ }
593
+
594
+ for(size_t i=0; i < stochastic_selection_indices.size() && next_beam_states_temp.size() < static_cast<size_t>(beam_width); ++i) {
595
+ next_beam_states_temp.push_back(candidates_pool[stochastic_selection_indices[i]]);
596
+ }
597
+ }
598
+
599
+ current_beam = next_beam_states_temp;
600
+ if (current_beam.empty()) break;
601
+ }
602
+
603
+ std::cout << overall_best_path_str << std::endl;
604
+
605
+ return 0;
606
+ }
607
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc015/best_program.cpp ADDED
@@ -0,0 +1,664 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include <string>
5
+ #include <array>
6
+ #include <numeric>
7
+ #include <algorithm>
8
+ #include <cmath>
9
+ #include <limits>
10
+ #include <chrono> // For seeding RNG
11
+ #include <unordered_map>
12
+ // #include <iomanip> // For debugging output
13
+
14
+ // Constants
15
+ const int GRID_SIZE = 10;
16
+ const int NUM_TURNS = 100;
17
+ const int NUM_FLAVORS = 3; // Flavors are 1, 2, 3
18
+
19
+ // Directions: F, B, L, R (Up, Down, Left, Right on typical grid with (0,0) top-left)
20
+ const int DR[] = {-1, 1, 0, 0};
21
+ const int DC[] = {0, 0, -1, 1};
22
+ const char DIR_CHARS[] = {'F', 'B', 'L', 'R'};
23
+ const int NUM_DIRECTIONS = 4;
24
+
25
+ // Global data initialized once
26
+ std::array<int, NUM_TURNS> G_FLAVOR_SEQUENCE;
27
+ std::array<int, NUM_FLAVORS + 1> G_flavor_total_counts;
28
+ std::array<std::pair<int, int>, NUM_FLAVORS + 1> G_target_col_ranges;
29
+ std::array<bool, NUM_FLAVORS + 1> G_flavor_active;
30
+ int G_last_dir_idx = -1; // -1 indicates no previous tilt; used for axis continuity bias
31
+
32
+ /* Docstring: Enable lightweight expectimax lookahead (up to depth 2).
33
+ We sample a few random placements of the next candy and, for each,
34
+ evaluate the best next tilt. At depth 2 we also sample one more step.
35
+ Depth 0 returns immediate board eval. */
36
+ // Lookahead parameters
37
+ const int MAX_LOOKAHEAD_DEPTH = 2;
38
+ // Sample count per depth (depth 1 then depth 2). Still very fast on 10x10.
39
+ static constexpr std::array<int, MAX_LOOKAHEAD_DEPTH> NUM_SAMPLES_CONFIG = {24, 12};
40
+
41
+
42
+ struct XorshiftRNG {
43
+ uint64_t x;
44
+ XorshiftRNG() : x(std::chrono::steady_clock::now().time_since_epoch().count()) {}
45
+
46
+ uint64_t next() {
47
+ x ^= x << 13;
48
+ x ^= x >> 7;
49
+ x ^= x << 17;
50
+ return x;
51
+ }
52
+
53
+ int uniform_int(int min_val, int max_val) {
54
+ if (min_val > max_val) return min_val;
55
+ if (min_val == max_val) return min_val;
56
+ uint64_t range = static_cast<uint64_t>(max_val) - min_val + 1;
57
+ return min_val + static_cast<int>(next() % range);
58
+ }
59
+ };
60
+ XorshiftRNG rng;
61
+
62
+ // Zobrist hashing and a small transposition table to memoize lookahead values.
63
+ // This greatly reduces duplicate computations from convergent tilt sequences.
64
+ std::array<std::array<std::array<uint64_t, NUM_FLAVORS + 1>, GRID_SIZE>, GRID_SIZE> G_zobrist;
65
+ static inline uint64_t compute_board_hash(const std::array<std::array<int, GRID_SIZE>, GRID_SIZE>& board) {
66
+ uint64_t h = 0;
67
+ for (int r = 0; r < GRID_SIZE; ++r) {
68
+ for (int c = 0; c < GRID_SIZE; ++c) {
69
+ int v = board[r][c];
70
+ if (v) h ^= G_zobrist[r][c][v];
71
+ }
72
+ }
73
+ return h;
74
+ }
75
+ // Transposition table keyed by (board hash, turn, depth)
76
+ static std::unordered_map<uint64_t, double> G_TT;
77
+ static constexpr size_t G_TT_MAX = 800000;
78
+
79
+
80
+ struct Candy {
81
+ int r, c, flavor;
82
+ };
83
+
84
+ struct GameState {
85
+ std::array<std::array<int, GRID_SIZE>, GRID_SIZE> board;
86
+ std::vector<Candy> candies_list;
87
+ int turn_num_1_indexed;
88
+
89
+ GameState() : turn_num_1_indexed(0) {
90
+ for (int i = 0; i < GRID_SIZE; ++i) {
91
+ board[i].fill(0);
92
+ }
93
+ candies_list.reserve(NUM_TURNS);
94
+ }
95
+
96
+ GameState(const GameState& other) = default;
97
+ GameState& operator=(const GameState& other) = default;
98
+ GameState(GameState&& other) noexcept = default;
99
+ GameState& operator=(GameState&& other) noexcept = default;
100
+
101
+ void place_candy(int r, int c, int flavor) {
102
+ board[r][c] = flavor;
103
+ candies_list.push_back({r, c, flavor});
104
+ }
105
+
106
+ std::pair<int, int> find_pth_empty_cell(int p_1_indexed) const {
107
+ int count = 0;
108
+ for (int r_idx = 0; r_idx < GRID_SIZE; ++r_idx) {
109
+ for (int c_idx = 0; c_idx < GRID_SIZE; ++c_idx) {
110
+ if (board[r_idx][c_idx] == 0) {
111
+ count++;
112
+ if (count == p_1_indexed) {
113
+ return {r_idx, c_idx};
114
+ }
115
+ }
116
+ }
117
+ }
118
+ return {-1, -1};
119
+ }
120
+
121
+ int count_empty_cells() const {
122
+ return GRID_SIZE * GRID_SIZE - static_cast<int>(candies_list.size());
123
+ }
124
+
125
+ /* Docstring: Apply a tilt by compacting each row/column toward the target edge.
126
+ Returns whether any candy actually moved. We intentionally do NOT rebuild
127
+ candies_list here; later evaluations scan the board directly, and the count
128
+ of candies (for empty-cell computation) remains correct since tilt doesn't
129
+ change it. */
130
+ bool apply_tilt(int dir_idx) {
131
+ bool changed = false;
132
+ if (dir_idx == 0) { // F (Up)
133
+ for (int c = 0; c < GRID_SIZE; ++c) {
134
+ int current_write_r = 0;
135
+ for (int r = 0; r < GRID_SIZE; ++r) {
136
+ int v = board[r][c];
137
+ if (v != 0) {
138
+ if (r != current_write_r) {
139
+ board[current_write_r][c] = v;
140
+ board[r][c] = 0;
141
+ changed = true;
142
+ }
143
+ current_write_r++;
144
+ }
145
+ }
146
+ }
147
+ } else if (dir_idx == 1) { // B (Down)
148
+ for (int c = 0; c < GRID_SIZE; ++c) {
149
+ int current_write_r = GRID_SIZE - 1;
150
+ for (int r = GRID_SIZE - 1; r >= 0; --r) {
151
+ int v = board[r][c];
152
+ if (v != 0) {
153
+ if (r != current_write_r) {
154
+ board[current_write_r][c] = v;
155
+ board[r][c] = 0;
156
+ changed = true;
157
+ }
158
+ current_write_r--;
159
+ }
160
+ }
161
+ }
162
+ } else if (dir_idx == 2) { // L (Left)
163
+ for (int r = 0; r < GRID_SIZE; ++r) {
164
+ int current_write_c = 0;
165
+ for (int c = 0; c < GRID_SIZE; ++c) {
166
+ int v = board[r][c];
167
+ if (v != 0) {
168
+ if (c != current_write_c) {
169
+ board[r][current_write_c] = v;
170
+ board[r][c] = 0;
171
+ changed = true;
172
+ }
173
+ current_write_c++;
174
+ }
175
+ }
176
+ }
177
+ } else { // R (Right, dir_idx == 3)
178
+ for (int r = 0; r < GRID_SIZE; ++r) {
179
+ int current_write_c = GRID_SIZE - 1;
180
+ for (int c = GRID_SIZE - 1; c >= 0; --c) {
181
+ int v = board[r][c];
182
+ if (v != 0) {
183
+ if (c != current_write_c) {
184
+ board[r][current_write_c] = v;
185
+ board[r][c] = 0;
186
+ changed = true;
187
+ }
188
+ current_write_c--;
189
+ }
190
+ }
191
+ }
192
+ }
193
+ return changed;
194
+ }
195
+
196
+ void rebuild_candies_list_from_board() {
197
+ candies_list.clear();
198
+ for (int r_idx = 0; r_idx < GRID_SIZE; ++r_idx) {
199
+ for (int c_idx = 0; c_idx < GRID_SIZE; ++c_idx) {
200
+ if (board[r_idx][c_idx] != 0) {
201
+ candies_list.push_back({r_idx, c_idx, board[r_idx][c_idx]});
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ long long calculate_sum_sq_comp_size() const {
208
+ long long total_sq_sum = 0;
209
+ std::array<std::array<bool, GRID_SIZE>, GRID_SIZE> visited;
210
+ for (int i = 0; i < GRID_SIZE; ++i) visited[i].fill(false);
211
+
212
+ std::array<std::pair<int, int>, GRID_SIZE * GRID_SIZE> q_arr;
213
+
214
+ for (int r_start = 0; r_start < GRID_SIZE; ++r_start) {
215
+ for (int c_start = 0; c_start < GRID_SIZE; ++c_start) {
216
+ if (board[r_start][c_start] != 0 && !visited[r_start][c_start]) {
217
+ int current_flavor = board[r_start][c_start];
218
+ long long current_comp_size = 0;
219
+
220
+ q_arr[0] = {r_start, c_start};
221
+ visited[r_start][c_start] = true;
222
+ int head = 0;
223
+ int tail = 1;
224
+
225
+ while(head < tail){
226
+ current_comp_size++;
227
+ const std::pair<int,int>& curr_cell = q_arr[head];
228
+ const int curr_r = curr_cell.first;
229
+ const int curr_c = curr_cell.second;
230
+ head++;
231
+
232
+ for (int i = 0; i < NUM_DIRECTIONS; ++i) {
233
+ int nr = curr_r + DR[i];
234
+ int nc = curr_c + DC[i];
235
+ if (nr >= 0 && nr < GRID_SIZE && nc >= 0 && nc < GRID_SIZE &&
236
+ !visited[nr][nc] && board[nr][nc] == current_flavor) {
237
+ visited[nr][nc] = true;
238
+ q_arr[tail++] = {nr, nc};
239
+ }
240
+ }
241
+ }
242
+ total_sq_sum += current_comp_size * current_comp_size;
243
+ }
244
+ }
245
+ }
246
+ return total_sq_sum;
247
+ }
248
+
249
+ /* Docstring: Compute per-flavor dispersion as sum of Manhattan distances
250
+ from each candy to its flavor's center-of-mass. Scan the board directly
251
+ so this remains correct even when candies_list positions are stale. */
252
+ double calculate_distance_penalty_CoM() const {
253
+ std::array<double, NUM_FLAVORS + 1> sum_r; sum_r.fill(0.0);
254
+ std::array<double, NUM_FLAVORS + 1> sum_c; sum_c.fill(0.0);
255
+ std::array<int, NUM_FLAVORS + 1> counts; counts.fill(0);
256
+
257
+ for (int r = 0; r < GRID_SIZE; ++r) {
258
+ for (int c = 0; c < GRID_SIZE; ++c) {
259
+ int v = board[r][c];
260
+ if (v == 0) continue;
261
+ counts[v]++;
262
+ sum_r[v] += r;
263
+ sum_c[v] += c;
264
+ }
265
+ }
266
+
267
+ std::array<std::pair<double, double>, NUM_FLAVORS + 1> com_coords;
268
+ for (int fl = 1; fl <= NUM_FLAVORS; ++fl) {
269
+ if (counts[fl] > 0) {
270
+ com_coords[fl] = {sum_r[fl] / counts[fl], sum_c[fl] / counts[fl]};
271
+ }
272
+ }
273
+
274
+ double total_manhattan_dist_penalty = 0.0;
275
+ for (int r = 0; r < GRID_SIZE; ++r) {
276
+ for (int c = 0; c < GRID_SIZE; ++c) {
277
+ int v = board[r][c];
278
+ if (v == 0) continue;
279
+ if (counts[v] > 1) {
280
+ const auto& com = com_coords[v];
281
+ total_manhattan_dist_penalty += std::abs(static_cast<double>(r) - com.first)
282
+ + std::abs(static_cast<double>(c) - com.second);
283
+ }
284
+ }
285
+ }
286
+ return total_manhattan_dist_penalty;
287
+ }
288
+
289
+ /* Docstring: Penalty for candies that lie outside their assigned
290
+ flavor column strip. Scan the board for robustness under simulated tilts. */
291
+ double calculate_region_penalty() const {
292
+ double penalty = 0.0;
293
+ for (int r = 0; r < GRID_SIZE; ++r) {
294
+ for (int c = 0; c < GRID_SIZE; ++c) {
295
+ int v = board[r][c];
296
+ if (v == 0) continue;
297
+ if (!G_flavor_active[v]) continue;
298
+
299
+ const auto& range = G_target_col_ranges[v];
300
+ int min_target_c = range.first;
301
+ int max_target_c = range.second;
302
+ if (min_target_c > max_target_c) continue;
303
+
304
+ if (c < min_target_c) penalty += (min_target_c - c);
305
+ else if (c > max_target_c) penalty += (c - max_target_c);
306
+ }
307
+ }
308
+ return penalty;
309
+ }
310
+
311
+ /* Docstring: Small bonus inside the correct strip for being on outer edges
312
+ and corners; encourages compact blobs aligned to boundaries. */
313
+ double calculate_edge_bonus() const {
314
+ double bonus_val = 0.0;
315
+ const double PER_CANDY_BONUS_FACTOR = 0.5;
316
+
317
+ for (int r = 0; r < GRID_SIZE; ++r) {
318
+ for (int c = 0; c < GRID_SIZE; ++c) {
319
+ int v = board[r][c];
320
+ if (v == 0) continue;
321
+ if (!G_flavor_active[v]) continue;
322
+
323
+ const auto& range = G_target_col_ranges[v];
324
+ int min_target_c = range.first;
325
+ int max_target_c = range.second;
326
+ if (min_target_c > max_target_c) continue;
327
+
328
+ bool in_correct_strip = (c >= min_target_c && c <= max_target_c);
329
+ if (!in_correct_strip) continue;
330
+
331
+ if (r == 0 || r == GRID_SIZE - 1) {
332
+ bonus_val += PER_CANDY_BONUS_FACTOR;
333
+ }
334
+ if ((c == 0 && min_target_c == 0) ||
335
+ (c == GRID_SIZE - 1 && max_target_c == GRID_SIZE - 1)) {
336
+ bonus_val += PER_CANDY_BONUS_FACTOR;
337
+ }
338
+ }
339
+ }
340
+ return bonus_val;
341
+ }
342
+
343
+ /* Docstring: Heuristic evaluation combining:
344
+ - sum of squares of same-flavor connected components (BFS),
345
+ - per-flavor center-of-mass Manhattan dispersion penalty,
346
+ - penalty for candies outside their assigned column strip,
347
+ - small edge/corner bonus inside correct strip,
348
+ - same-flavor adjacency pairs bonus (cohesion),
349
+ - interface penalty for different-flavor touching pairs (perimeter control).
350
+ Coefficients vary with turn to emphasize connectivity later while using
351
+ local cues early; scales are conservative to avoid overfitting. */
352
+ double evaluate() const {
353
+ if (turn_num_1_indexed == 0) return 0.0;
354
+
355
+ long long sum_sq_comp = calculate_sum_sq_comp_size();
356
+ double dist_penalty_com = calculate_distance_penalty_CoM();
357
+ double region_penalty_val = calculate_region_penalty();
358
+ double edge_bonus_val = calculate_edge_bonus();
359
+
360
+ // Count adjacent pairs (right/down only to avoid double-counting)
361
+ int adjacency_pairs = 0;
362
+ int mismatch_pairs = 0;
363
+ for (int r = 0; r < GRID_SIZE; ++r) {
364
+ for (int c = 0; c < GRID_SIZE; ++c) {
365
+ int v = board[r][c];
366
+ if (v == 0) continue;
367
+ if (r + 1 < GRID_SIZE) {
368
+ int w = board[r + 1][c];
369
+ if (w == v) adjacency_pairs++;
370
+ else if (w != 0) mismatch_pairs++;
371
+ }
372
+ if (c + 1 < GRID_SIZE) {
373
+ int w = board[r][c + 1];
374
+ if (w == v) adjacency_pairs++;
375
+ else if (w != 0) mismatch_pairs++;
376
+ }
377
+ }
378
+ }
379
+
380
+ double current_turn_double = static_cast<double>(turn_num_1_indexed);
381
+
382
+ double A_coeff_conn = 15.0 + 1.1 * current_turn_double;
383
+ double B_coeff_com_base = std::max(0.0, 170.0 - 1.7 * current_turn_double);
384
+ double C_coeff_region_penalty_direct = std::max(2.0, 27.0 - 0.17 * current_turn_double);
385
+ double D_coeff_edge_bonus = 5.0 + 0.2 * current_turn_double;
386
+ double E_coeff_adjacency = 180.0 + 3.0 * current_turn_double;
387
+ // Penalize heterogeneous boundaries to reduce perimeter and mixing
388
+ double F_coeff_mismatch = 120.0 + 2.0 * current_turn_double;
389
+
390
+ return A_coeff_conn * sum_sq_comp
391
+ - B_coeff_com_base * dist_penalty_com
392
+ - C_coeff_region_penalty_direct * region_penalty_val
393
+ + D_coeff_edge_bonus * edge_bonus_val
394
+ + E_coeff_adjacency * adjacency_pairs
395
+ - F_coeff_mismatch * mismatch_pairs;
396
+ }
397
+ };
398
+
399
+
400
+
401
+ /* Docstring: Expectimax-style lookahead evaluator with memoization (TT).
402
+ - Base cases: return immediate evaluation.
403
+ - Otherwise, sample a few next placements uniformly (without replacement),
404
+ maximize over the next tilt for each sample, and average.
405
+ - Use a Zobrist-hash keyed transposition table on (board, turn, depth)
406
+ to avoid recomputing converged states across branches. */
407
+ double eval_lookahead(const GameState& state_after_tilt, int turn_T_of_candy_just_processed, int depth_remaining) {
408
+ if (depth_remaining == 0 || turn_T_of_candy_just_processed == NUM_TURNS) {
409
+ return state_after_tilt.evaluate();
410
+ }
411
+
412
+ int num_empty = state_after_tilt.count_empty_cells();
413
+ if (num_empty == 0) {
414
+ return state_after_tilt.evaluate();
415
+ }
416
+
417
+ // Probe cache
418
+ uint64_t h = compute_board_hash(state_after_tilt.board);
419
+ // Mix in turn and depth to distinguish future stochastic contexts
420
+ uint64_t key = h
421
+ ^ (uint64_t(turn_T_of_candy_just_processed) * 0x9E3779B97F4A7C15ULL)
422
+ ^ (uint64_t(depth_remaining) * 0xC2B2AE3D27D4EB4FULL);
423
+ auto it = G_TT.find(key);
424
+ if (it != G_TT.end()) return it->second;
425
+
426
+ int next_candy_flavor = G_FLAVOR_SEQUENCE[turn_T_of_candy_just_processed];
427
+ int sample_count_param_idx = MAX_LOOKAHEAD_DEPTH - depth_remaining;
428
+ int sample_count_this_depth = NUM_SAMPLES_CONFIG[sample_count_param_idx];
429
+ int actual_num_samples = std::min(sample_count_this_depth, num_empty);
430
+
431
+ if (actual_num_samples == 0) {
432
+ double base = state_after_tilt.evaluate();
433
+ if (G_TT.size() > G_TT_MAX) G_TT.clear();
434
+ G_TT.emplace(key, base);
435
+ return base;
436
+ }
437
+
438
+ double sum_over_sampled_placements = 0.0;
439
+
440
+ if (actual_num_samples == num_empty) {
441
+ // Enumerate all empty cells deterministically (1..num_empty)
442
+ for (int s = 0; s < actual_num_samples; ++s) {
443
+ int p_val_1_indexed_sample = s + 1;
444
+
445
+ GameState S_after_placement = state_after_tilt;
446
+ std::pair<int, int> candy_loc = S_after_placement.find_pth_empty_cell(p_val_1_indexed_sample);
447
+ S_after_placement.place_candy(candy_loc.first, candy_loc.second, next_candy_flavor);
448
+ S_after_placement.turn_num_1_indexed = turn_T_of_candy_just_processed + 1;
449
+
450
+ double max_eval_for_this_placement = std::numeric_limits<double>::lowest();
451
+ for (int dir_idx_next_tilt = 0; dir_idx_next_tilt < NUM_DIRECTIONS; ++dir_idx_next_tilt) {
452
+ GameState S_after_next_tilt = S_after_placement;
453
+ (void)S_after_next_tilt.apply_tilt(dir_idx_next_tilt);
454
+ double val = eval_lookahead(S_after_next_tilt, S_after_placement.turn_num_1_indexed, depth_remaining - 1);
455
+ if (val > max_eval_for_this_placement) {
456
+ max_eval_for_this_placement = val;
457
+ }
458
+ }
459
+ sum_over_sampled_placements += max_eval_for_this_placement;
460
+ }
461
+ } else {
462
+ // Deterministic stratified sampling across the empty-cell index space
463
+ for (int s = 0; s < actual_num_samples; ++s) {
464
+ int x = static_cast<int>(((s + 0.5) * num_empty) / actual_num_samples) + 1;
465
+ if (x < 1) x = 1;
466
+ if (x > num_empty) x = num_empty;
467
+
468
+ GameState S_after_placement = state_after_tilt;
469
+ std::pair<int, int> candy_loc = S_after_placement.find_pth_empty_cell(x);
470
+ S_after_placement.place_candy(candy_loc.first, candy_loc.second, next_candy_flavor);
471
+ S_after_placement.turn_num_1_indexed = turn_T_of_candy_just_processed + 1;
472
+
473
+ double max_eval_for_this_placement = std::numeric_limits<double>::lowest();
474
+ for (int dir_idx_next_tilt = 0; dir_idx_next_tilt < NUM_DIRECTIONS; ++dir_idx_next_tilt) {
475
+ GameState S_after_next_tilt = S_after_placement;
476
+ (void)S_after_next_tilt.apply_tilt(dir_idx_next_tilt);
477
+ double val = eval_lookahead(S_after_next_tilt, S_after_placement.turn_num_1_indexed, depth_remaining - 1);
478
+ if (val > max_eval_for_this_placement) {
479
+ max_eval_for_this_placement = val;
480
+ }
481
+ }
482
+ sum_over_sampled_placements += max_eval_for_this_placement;
483
+ }
484
+ }
485
+
486
+ double result = sum_over_sampled_placements / actual_num_samples;
487
+ if (G_TT.size() > G_TT_MAX) G_TT.clear();
488
+ G_TT.emplace(key, result);
489
+ return result;
490
+ }
491
+
492
+ /* Docstring: Choose the best tilt among the 4 directions using expectimax
493
+ (up to depth 2): simulate each tilt, then look ahead by sampling the next
494
+ placement(s) and maximizing over the next tilt(s).
495
+ Tie-breaking bias (tiny, deterministic):
496
+ - prefer continuing the same axis (F/B vs L/R)
497
+ - prefer exactly the same direction as last tilt
498
+ - in early turns, slightly prefer F/L to build a top-left corner.
499
+ The bias is tiny so it only acts as tie-breaker. */
500
+ char decide_tilt_direction_logic(const GameState& current_gs_after_placement) {
501
+ double best_score_with_bias = std::numeric_limits<double>::lowest();
502
+ int best_dir_idx = 0;
503
+
504
+ int turn_T_for_lookahead_base = current_gs_after_placement.turn_num_1_indexed;
505
+
506
+ for (int i = 0; i < NUM_DIRECTIONS; ++i) {
507
+ GameState gs_after_tilt_T = current_gs_after_placement;
508
+ bool changed_by_tilt = gs_after_tilt_T.apply_tilt(i);
509
+
510
+ double base_eval = eval_lookahead(gs_after_tilt_T, turn_T_for_lookahead_base, MAX_LOOKAHEAD_DEPTH);
511
+
512
+ // Tiny deterministic bias for tie-breaking
513
+ double bias = 0.0;
514
+ if (G_last_dir_idx >= 0) {
515
+ bool same_axis = ((i < 2) == (G_last_dir_idx < 2));
516
+ if (same_axis) bias += 1e-9;
517
+ if (i == G_last_dir_idx) bias += 2e-9;
518
+ }
519
+ if (turn_T_for_lookahead_base <= 35) {
520
+ if (i == 0 || i == 2) bias += 5e-10; // early preference for F/L
521
+ }
522
+ // Prefer tilts that actually move candies (avoid wasting a move)
523
+ if (!changed_by_tilt) bias -= 5e-10;
524
+
525
+ double eval_with_bias = base_eval + bias;
526
+ if (eval_with_bias > best_score_with_bias) {
527
+ best_score_with_bias = eval_with_bias;
528
+ best_dir_idx = i;
529
+ }
530
+ }
531
+ return DIR_CHARS[best_dir_idx];
532
+ }
533
+
534
+
535
+
536
+
537
+
538
+
539
+
540
+
541
+
542
+ void initialize_global_data() {
543
+ G_flavor_total_counts.fill(0);
544
+ for (int t = 0; t < NUM_TURNS; ++t) {
545
+ std::cin >> G_FLAVOR_SEQUENCE[t];
546
+ G_flavor_total_counts[G_FLAVOR_SEQUENCE[t]]++;
547
+ }
548
+
549
+ // Deterministic RNG seed derived from the full flavor sequence.
550
+ // This stabilizes decisions across runs on the same input.
551
+ uint64_t seed = 0x9E3779B97F4A7C15ULL;
552
+ for (int t = 0; t < NUM_TURNS; ++t) {
553
+ seed ^= static_cast<uint64_t>(G_FLAVOR_SEQUENCE[t]) + 0x9E3779B97F4A7C15ULL + (seed << 6) + (seed >> 2);
554
+ }
555
+ rng.x = seed | 1ULL;
556
+
557
+ // Initialize zobrist table and reserve TT
558
+ for (int r = 0; r < GRID_SIZE; ++r) {
559
+ for (int c = 0; c < GRID_SIZE; ++c) {
560
+ for (int v = 1; v <= NUM_FLAVORS; ++v) {
561
+ G_zobrist[r][c][v] = rng.next();
562
+ }
563
+ }
564
+ }
565
+ G_TT.clear();
566
+ G_TT.reserve(1 << 20);
567
+
568
+ G_flavor_active.fill(false);
569
+ std::vector<std::pair<int, int>> sorter_flavor_count_id;
570
+ for (int fl = 1; fl <= NUM_FLAVORS; ++fl) {
571
+ if (G_flavor_total_counts[fl] > 0) {
572
+ G_flavor_active[fl] = true;
573
+ sorter_flavor_count_id.push_back({G_flavor_total_counts[fl], fl});
574
+ }
575
+ }
576
+ std::sort(sorter_flavor_count_id.begin(), sorter_flavor_count_id.end(),
577
+ [](const std::pair<int, int>& a, const std::pair<int, int>& b) {
578
+ if (a.first != b.first) {
579
+ return a.first > b.first;
580
+ }
581
+ return a.second < b.second;
582
+ });
583
+
584
+ std::vector<int> active_flavor_ids_sorted_by_priority;
585
+ for (const auto& p : sorter_flavor_count_id) {
586
+ active_flavor_ids_sorted_by_priority.push_back(p.second);
587
+ }
588
+
589
+ std::vector<int> assigned_widths(NUM_FLAVORS + 1, 0);
590
+ int total_assigned_width_sum = 0;
591
+
592
+ if (!active_flavor_ids_sorted_by_priority.empty()) {
593
+ double total_candies_for_proportion = 0;
594
+ for (int fl_id : active_flavor_ids_sorted_by_priority) {
595
+ total_candies_for_proportion += G_flavor_total_counts[fl_id];
596
+ }
597
+ if (total_candies_for_proportion == 0) total_candies_for_proportion = 1;
598
+
599
+ for (int fl_id : active_flavor_ids_sorted_by_priority) {
600
+ assigned_widths[fl_id] = static_cast<int>(std::floor(
601
+ static_cast<double>(GRID_SIZE) * G_flavor_total_counts[fl_id] / total_candies_for_proportion
602
+ ));
603
+ total_assigned_width_sum += assigned_widths[fl_id];
604
+ }
605
+
606
+ int remaining_width_to_assign = GRID_SIZE - total_assigned_width_sum;
607
+ for (int i = 0; i < remaining_width_to_assign; ++i) {
608
+ assigned_widths[active_flavor_ids_sorted_by_priority[i % active_flavor_ids_sorted_by_priority.size()]]++;
609
+ }
610
+ }
611
+
612
+ int current_col_start = 0;
613
+ for (int fl_id_in_sorted_order : active_flavor_ids_sorted_by_priority) {
614
+ if (assigned_widths[fl_id_in_sorted_order] > 0) {
615
+ G_target_col_ranges[fl_id_in_sorted_order] = {current_col_start, current_col_start + assigned_widths[fl_id_in_sorted_order] - 1};
616
+ current_col_start += assigned_widths[fl_id_in_sorted_order];
617
+ } else {
618
+ G_target_col_ranges[fl_id_in_sorted_order] = {current_col_start, current_col_start - 1};
619
+ }
620
+ }
621
+
622
+ for (int fl = 1; fl <= NUM_FLAVORS; ++fl) {
623
+ if (!G_flavor_active[fl]) {
624
+ G_target_col_ranges[fl] = {0, -1};
625
+ }
626
+ }
627
+ }
628
+
629
+
630
+ int main() {
631
+ std::ios_base::sync_with_stdio(false);
632
+ std::cin.tie(NULL);
633
+
634
+ initialize_global_data();
635
+
636
+ GameState current_gs;
637
+ for (int t_0_indexed = 0; t_0_indexed < NUM_TURNS; ++t_0_indexed) {
638
+ current_gs.turn_num_1_indexed = t_0_indexed + 1;
639
+
640
+ int p_val_1_indexed;
641
+ std::cin >> p_val_1_indexed;
642
+
643
+ std::pair<int, int> candy_loc = current_gs.find_pth_empty_cell(p_val_1_indexed);
644
+
645
+ current_gs.place_candy(candy_loc.first, candy_loc.second, G_FLAVOR_SEQUENCE[t_0_indexed]);
646
+
647
+ char chosen_dir_char = decide_tilt_direction_logic(current_gs);
648
+
649
+ std::cout << chosen_dir_char << std::endl;
650
+
651
+ int dir_idx_to_apply = 0;
652
+ for(int k=0; k<NUM_DIRECTIONS; ++k) {
653
+ if(DIR_CHARS[k] == chosen_dir_char) {
654
+ dir_idx_to_apply = k;
655
+ break;
656
+ }
657
+ }
658
+ G_last_dir_idx = dir_idx_to_apply;
659
+ (void)current_gs.apply_tilt(dir_idx_to_apply);
660
+ }
661
+
662
+ return 0;
663
+ }
664
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc015/config.yaml ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ALE-Bench ahc015 — AtCoder Heuristic Contest
2
+ # Usage: skydiscover-run initial_program.cpp evaluator.py -c config.yaml -s <strategy>
3
+ language: cpp
4
+ diff_based_generation: true
5
+ max_iterations: 100
6
+ checkpoint_interval: 10
7
+ max_solution_length: 60000
8
+ llm:
9
+ api_base: https://api.openai.com/v1
10
+ models:
11
+ - name: "gpt-5"
12
+ weight: 1.0
13
+ max_tokens: 32000
14
+ timeout: 600
15
+ prompt:
16
+ system_message: "You are a world-class algorithm engineer, and you are very good at programming. Now, you are participating\
17
+ \ in a programming contest. You are asked to solve a heuristic problem, known as an NP-hard problem.\n\nStory\n--------\n\
18
+ AtCoder's CEO Takahashi prepares for Halloween tomorrow.\nAt AtCoder's Halloween party, Takahashi will dress up in disguise\
19
+ \ and receive a piece of candy from 100 employees in turn by saying, \"trick or treat!\"\nHe prepares a square box that\
20
+ \ can contain $10\\times 10$ pieces of candy in a grid pattern, and each employee puts a piece of candy in an empty space\
21
+ \ so that no candies overlap.\nThere are $3$ types of candies: strawberry, watermelon, and pumpkin flavors.\nHe knows\
22
+ \ which flavor of candy each employee will put in by preliminary survey, but he doesn't know where each employee will\
23
+ \ put it.\nSince he is a clean freak, he will move the pieces of candy by tilting the box forward, backward, left, or\
24
+ \ right, just once for each piece of candy received, and eventually wants to make sure that the same type of candy is\
25
+ \ clustered together as much as possible.\nPlease help him by writing a program to determine the direction to tilt.\n\n\
26
+ <figure>\n <img src=\"./images/b639c75d_1.gif\">\n <figcaption>Example for $5\\times 5$</figcaption>\n</figure>\n\n\n\
27
+ Problem Statement\n--------\nThere is a box that can contain $10\\times 10$ pieces of candy in a grid pattern.\nThe box\
28
+ \ is initially empty, and $100$ pieces of candy will be placed in order.\nThere are $3$ flavors of candy, and we know\
29
+ \ in advance the flavor $f_t (1\\leq f_t\\leq 3)$ of the $t$-th candy.\nOn the other hand, we do not know in advance to\
30
+ \ which cell each candy will be placed, and it will be chosen uniformly at random among the empty cells.\nYou cannot change\
31
+ \ the order in which the pieces of candy are received.\n\nEach time you receive one piece of candy, you must tilt the\
32
+ \ box forward, backward, left, or right exactly once.\nWhen you tilt the box, each piece of candy moves in that direction\
33
+ \ simultaneously until it reaches the edge or hits another candy.\nFor example, if you tilt the box forward in the state\
34
+ \ shown in the left figure, the box will be in the state shown in the right figure.\n\n![](./images/b639c75d_2.png)\n\
35
+ ![](./images/b639c75d_3.png)\n\nScoring\n--------\nWe define the connectivity of pieces of candy as follows and consider\
36
+ \ the connected components.\n> Two pieces of candy are connected if and only if they are of the same flavor and can reach\
37
+ \ each other through only pieces of candy of the same flavor in the four directions (front, back, left, right).\n\nFor\
38
+ \ example, the state in the figure below consists of $7$ connected components of size $\\\\{1, 1, 2, 2, 4, 6, 9\\\\}$.\n\
39
+ \n![](./images/b639c75d_4.png)\n\nLet $n_1,\\cdots,n_k$ be the list of sizes of connected components in the final state\
40
+ \ after receiving 100 pieces of candy, and let $d_i$ be the total number of pieces of candy of flavor $i$.\nThen the score\
41
+ \ for the test case is\n\n\\\\[\\mathrm{round}\\left(10^6\\times\\frac{\\sum_{i=1}^k n_i^2}{\\sum_{i=1}^3 d_i^2}\\right)\\\
42
+ \\]\n\nYour task is to write a program to determine the tilting directions so that you can get as high a score as possible.\n\
43
+ \nThere are 200 test cases, and the score of a submission is the total score for each test case.\nIf your submission produces\
44
+ \ an illegal output or exceeds the time limit for some test cases, the submission itself will be judged as <span class='label\
45
+ \ label-warning' data-toggle='tooltip' data-placement='top' title=\"Wrong Answer\">WA</span> or <span class='label label-warning'\
46
+ \ data-toggle='tooltip' data-placement='top' title=\"Time Limit Exceeded\">TLE</span> , and the score of the submission\
47
+ \ will be zero.\nThe highest score obtained during the contest will determine the final ranking, and there will be no\
48
+ \ system test after the contest.\nIf more than one participant gets the same score, they will be ranked in the same place\
49
+ \ regardless of the submission time (note that this is changed from previous short-term AHCs).\n\n\nInput and Output\n\
50
+ --------\nFirst, the flavor of each piece of candy is given from Standard Input in the following format.\n~~~\n$f_1$ $f_2$\
51
+ \ $\\cdots$ $f_{100}$\n~~~\n\nEach $f_t$ is an integer value between $1$ and $3$, representing the flavor of the $t$-th\
52
+ \ piece of candy.\n\nAfter reading the above information, repeat the following process $100$ times.\n\nIn the $t$-th process\
53
+ \ ($1\\leq t\\leq 100$), a single integer $p_t$ between $1$ and $101-t$ is given from Standard Input.\nLet us number the\
54
+ \ empty cells from $1$ to $101-t$ in front-to-back and left-to-right priority, as shown in the example figure below.\n\
55
+ Then the $t$-th piece of candy is placed in the $p_t$-th empty cell.\n\n![](./images/b639c75d_5.png)\n\nAfter reading\
56
+ \ $p_t$, by representing forward, backward, left, and right by `F`, `B`, `L`, and `R`, respectively, output a single character\
57
+ \ to Standard Output to indicate which direction to tilt the box.\n\n<font color=\"red\">**The output must be followed\
58
+ \ by a new line, and you have to flush Standard Output.**</font>\nOtherwise, the submission might be judged as <span class='label\
59
+ \ label-warning' data-toggle='tooltip' data-placement='top' title=\"Time Limit Exceeded\">TLE</span>.\n<font color=\"\
60
+ red\">**Note that $p_{t+1}$ will not be given until you output the $t$-th direction.**</font>\nSince nothing happens at\
61
+ \ the 100th tilt, you may skip the output.\n\n#### Example\n\n<table class=\"table table-bordered\">\n<thead>\n<tr>\n\
62
+ <th>$t$</th>\n<th>Input</th>\n<th>Output</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Prior information</td>\n<td><pre>2\
63
+ \ 2 1 3 1 2 1 2 1 $\\cdots$ 3</pre></td>\n<td></td>\n</tr>\n<tr>\n<td>1</td>\n<td><pre>3</pre></td>\n<td><pre>R</pre></td>\n\
64
+ </tr>\n<tr>\n<td>2</td>\n<td><pre>98</pre></td>\n<td><pre>B</pre></td>\n</tr>\n<tr>\n<td>$\\vdots$</td>\n<td></td>\n<td></td>\n\
65
+ </tr>\n<tr>\n<td>100</td>\n<td><pre>1</pre></td>\n<td><pre>L</pre></td>\n</tr>\n</tbody>\n</table>\n\n\n<a href=\"https://img.atcoder.jp/ahc015/b639c75d.html?lang=en&seed=0&output=R%0D%0AB%0D%0AB%0D%0AR%0D%0AF%0D%0AR%0D%0AF%0D%0AR%0D%0AR%0D%0AF%0D%0AB%0D%0AL%0D%0AB%0D%0AL%0D%0AF%0D%0AF%0D%0AB%0D%0AF%0D%0AB%0D%0AL%0D%0AL%0D%0AL%0D%0AL%0D%0AB%0D%0AF%0D%0AF%0D%0AR%0D%0AR%0D%0AF%0D%0AL%0D%0AL%0D%0AB%0D%0AL%0D%0AL%0D%0AL%0D%0AB%0D%0AL%0D%0AR%0D%0AF%0D%0AL%0D%0AB%0D%0AL%0D%0AF%0D%0AF%0D%0AF%0D%0AL%0D%0AL%0D%0AR%0D%0AB%0D%0AB%0D%0AF%0D%0AL%0D%0AF%0D%0AR%0D%0AB%0D%0AL%0D%0AF%0D%0AF%0D%0AR%0D%0AL%0D%0AR%0D%0AL%0D%0AR%0D%0AR%0D%0AB%0D%0AR%0D%0AB%0D%0AR%0D%0AR%0D%0AF%0D%0AB%0D%0AF%0D%0AR%0D%0AR%0D%0AF%0D%0AB%0D%0AF%0D%0AB%0D%0AF%0D%0AR%0D%0AB%0D%0AF%0D%0AF%0D%0AF%0D%0AB%0D%0AB%0D%0AL%0D%0AL%0D%0AR%0D%0AF%0D%0AB%0D%0AL%0D%0AB%0D%0AF%0D%0AB%0D%0AR%0D%0AF%0D%0AF%0D%0AL%0D%0AL%0D%0A\"\
66
+ >Show example</a>\n\n\nInput Generation\n--------\nLet $\\mathrm{rand}(L,U)$ be a function that generates a uniform random\
67
+ \ integer between $L$ and $U$, inclusive.\nEach $f_t$ is generated by $\\mathrm{rand}(1,3)$.\nEach $p_t$ is generated\
68
+ \ by $\\mathrm{rand}(1,101-t)$.\n\n\nTools (Input generator and visualizer)\n--------\n- <a href=\"https://img.atcoder.jp/ahc015/b639c75d.html?lang=en\"\
69
+ >Web version</a>: This is more powerful than the local version providing animations and manual play.\n- <a href=\"https://img.atcoder.jp/ahc015/b639c75d.zip\"\
70
+ >Local version</a>: You need a compilation environment of <a href=\"https://www.rust-lang.org/\">Rust language</a>.\n\
71
+ \ - <a href=\"https://img.atcoder.jp/ahc015/b639c75d_windows.zip\">Pre-compiled binary for Windows</a>: If you are not\
72
+ \ familiar with the Rust language environment, please use this instead.\n\nPlease be aware that sharing visualization\
73
+ \ results or discussing solutions/ideas during the contest is prohibited.\n\n\nProblem constraints:\ntime_limit=2.0 memory_limit=1073741824\n"
74
+ evaluator:
75
+ timeout: 10000
76
+ cascade_evaluation: false
77
+
benchmarks/ale_bench/ale-bench-lite-problems/ahc015/initial_program.cpp ADDED
@@ -0,0 +1,491 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include <string>
5
+ #include <array>
6
+ #include <numeric>
7
+ #include <algorithm>
8
+ #include <cmath>
9
+ #include <limits>
10
+ #include <chrono> // For seeding RNG
11
+ // #include <iomanip> // For debugging output
12
+
13
+ // Constants
14
+ const int GRID_SIZE = 10;
15
+ const int NUM_TURNS = 100;
16
+ const int NUM_FLAVORS = 3; // Flavors are 1, 2, 3
17
+
18
+ // Directions: F, B, L, R (Up, Down, Left, Right on typical grid with (0,0) top-left)
19
+ const int DR[] = {-1, 1, 0, 0};
20
+ const int DC[] = {0, 0, -1, 1};
21
+ const char DIR_CHARS[] = {'F', 'B', 'L', 'R'};
22
+ const int NUM_DIRECTIONS = 4;
23
+
24
+ // Global data initialized once
25
+ std::array<int, NUM_TURNS> G_FLAVOR_SEQUENCE;
26
+ std::array<int, NUM_FLAVORS + 1> G_flavor_total_counts;
27
+ std::array<std::pair<int, int>, NUM_FLAVORS + 1> G_target_col_ranges;
28
+ std::array<bool, NUM_FLAVORS + 1> G_flavor_active;
29
+
30
+ // Lookahead parameters
31
+ const int MAX_LOOKAHEAD_DEPTH = 2;
32
+ // Final Iteration: Reverted to sample counts from Iteration 2, which scored highest.
33
+ static constexpr std::array<int, MAX_LOOKAHEAD_DEPTH> NUM_SAMPLES_CONFIG = {23, 9};
34
+
35
+
36
+ struct XorshiftRNG {
37
+ uint64_t x;
38
+ XorshiftRNG() : x(std::chrono::steady_clock::now().time_since_epoch().count()) {}
39
+
40
+ uint64_t next() {
41
+ x ^= x << 13;
42
+ x ^= x >> 7;
43
+ x ^= x << 17;
44
+ return x;
45
+ }
46
+
47
+ int uniform_int(int min_val, int max_val) {
48
+ if (min_val > max_val) return min_val;
49
+ if (min_val == max_val) return min_val;
50
+ uint64_t range = static_cast<uint64_t>(max_val) - min_val + 1;
51
+ return min_val + static_cast<int>(next() % range);
52
+ }
53
+ };
54
+ XorshiftRNG rng;
55
+
56
+
57
+ struct Candy {
58
+ int r, c, flavor;
59
+ };
60
+
61
+ struct GameState {
62
+ std::array<std::array<int, GRID_SIZE>, GRID_SIZE> board;
63
+ std::vector<Candy> candies_list;
64
+ int turn_num_1_indexed;
65
+
66
+ GameState() : turn_num_1_indexed(0) {
67
+ for (int i = 0; i < GRID_SIZE; ++i) {
68
+ board[i].fill(0);
69
+ }
70
+ candies_list.reserve(NUM_TURNS);
71
+ }
72
+
73
+ GameState(const GameState& other) = default;
74
+ GameState& operator=(const GameState& other) = default;
75
+ GameState(GameState&& other) noexcept = default;
76
+ GameState& operator=(GameState&& other) noexcept = default;
77
+
78
+ void place_candy(int r, int c, int flavor) {
79
+ board[r][c] = flavor;
80
+ candies_list.push_back({r, c, flavor});
81
+ }
82
+
83
+ std::pair<int, int> find_pth_empty_cell(int p_1_indexed) const {
84
+ int count = 0;
85
+ for (int r_idx = 0; r_idx < GRID_SIZE; ++r_idx) {
86
+ for (int c_idx = 0; c_idx < GRID_SIZE; ++c_idx) {
87
+ if (board[r_idx][c_idx] == 0) {
88
+ count++;
89
+ if (count == p_1_indexed) {
90
+ return {r_idx, c_idx};
91
+ }
92
+ }
93
+ }
94
+ }
95
+ return {-1, -1};
96
+ }
97
+
98
+ int count_empty_cells() const {
99
+ return GRID_SIZE * GRID_SIZE - static_cast<int>(candies_list.size());
100
+ }
101
+
102
+ void apply_tilt(int dir_idx) {
103
+ if (dir_idx == 0) { // F (Up)
104
+ for (int c = 0; c < GRID_SIZE; ++c) {
105
+ int current_write_r = 0;
106
+ for (int r = 0; r < GRID_SIZE; ++r) {
107
+ if (board[r][c] != 0) {
108
+ if (r != current_write_r) {
109
+ board[current_write_r][c] = board[r][c];
110
+ board[r][c] = 0;
111
+ }
112
+ current_write_r++;
113
+ }
114
+ }
115
+ }
116
+ } else if (dir_idx == 1) { // B (Down)
117
+ for (int c = 0; c < GRID_SIZE; ++c) {
118
+ int current_write_r = GRID_SIZE - 1;
119
+ for (int r = GRID_SIZE - 1; r >= 0; --r) {
120
+ if (board[r][c] != 0) {
121
+ if (r != current_write_r) {
122
+ board[current_write_r][c] = board[r][c];
123
+ board[r][c] = 0;
124
+ }
125
+ current_write_r--;
126
+ }
127
+ }
128
+ }
129
+ } else if (dir_idx == 2) { // L (Left)
130
+ for (int r = 0; r < GRID_SIZE; ++r) {
131
+ int current_write_c = 0;
132
+ for (int c = 0; c < GRID_SIZE; ++c) {
133
+ if (board[r][c] != 0) {
134
+ if (c != current_write_c) {
135
+ board[r][current_write_c] = board[r][c];
136
+ board[r][c] = 0;
137
+ }
138
+ current_write_c++;
139
+ }
140
+ }
141
+ }
142
+ } else { // R (Right, dir_idx == 3)
143
+ for (int r = 0; r < GRID_SIZE; ++r) {
144
+ int current_write_c = GRID_SIZE - 1;
145
+ for (int c = GRID_SIZE - 1; c >= 0; --c) {
146
+ if (board[r][c] != 0) {
147
+ if (c != current_write_c) {
148
+ board[r][current_write_c] = board[r][c];
149
+ board[r][c] = 0;
150
+ }
151
+ current_write_c--;
152
+ }
153
+ }
154
+ }
155
+ }
156
+ rebuild_candies_list_from_board();
157
+ }
158
+
159
+ void rebuild_candies_list_from_board() {
160
+ candies_list.clear();
161
+ for (int r_idx = 0; r_idx < GRID_SIZE; ++r_idx) {
162
+ for (int c_idx = 0; c_idx < GRID_SIZE; ++c_idx) {
163
+ if (board[r_idx][c_idx] != 0) {
164
+ candies_list.push_back({r_idx, c_idx, board[r_idx][c_idx]});
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ long long calculate_sum_sq_comp_size() const {
171
+ long long total_sq_sum = 0;
172
+ std::array<std::array<bool, GRID_SIZE>, GRID_SIZE> visited;
173
+ for (int i = 0; i < GRID_SIZE; ++i) visited[i].fill(false);
174
+
175
+ std::array<std::pair<int, int>, GRID_SIZE * GRID_SIZE> q_arr;
176
+
177
+ for (int r_start = 0; r_start < GRID_SIZE; ++r_start) {
178
+ for (int c_start = 0; c_start < GRID_SIZE; ++c_start) {
179
+ if (board[r_start][c_start] != 0 && !visited[r_start][c_start]) {
180
+ int current_flavor = board[r_start][c_start];
181
+ long long current_comp_size = 0;
182
+
183
+ q_arr[0] = {r_start, c_start};
184
+ visited[r_start][c_start] = true;
185
+ int head = 0;
186
+ int tail = 1;
187
+
188
+ while(head < tail){
189
+ current_comp_size++;
190
+ const std::pair<int,int>& curr_cell = q_arr[head];
191
+ const int curr_r = curr_cell.first;
192
+ const int curr_c = curr_cell.second;
193
+ head++;
194
+
195
+ for (int i = 0; i < NUM_DIRECTIONS; ++i) {
196
+ int nr = curr_r + DR[i];
197
+ int nc = curr_c + DC[i];
198
+ if (nr >= 0 && nr < GRID_SIZE && nc >= 0 && nc < GRID_SIZE &&
199
+ !visited[nr][nc] && board[nr][nc] == current_flavor) {
200
+ visited[nr][nc] = true;
201
+ q_arr[tail++] = {nr, nc};
202
+ }
203
+ }
204
+ }
205
+ total_sq_sum += current_comp_size * current_comp_size;
206
+ }
207
+ }
208
+ }
209
+ return total_sq_sum;
210
+ }
211
+
212
+ double calculate_distance_penalty_CoM() const {
213
+ if (candies_list.empty()) return 0.0;
214
+
215
+ std::array<double, NUM_FLAVORS + 1> sum_r; sum_r.fill(0.0);
216
+ std::array<double, NUM_FLAVORS + 1> sum_c; sum_c.fill(0.0);
217
+ std::array<int, NUM_FLAVORS + 1> counts; counts.fill(0);
218
+
219
+ for (const auto& candy : candies_list) {
220
+ counts[candy.flavor]++;
221
+ sum_r[candy.flavor] += candy.r;
222
+ sum_c[candy.flavor] += candy.c;
223
+ }
224
+
225
+ std::array<std::pair<double, double>, NUM_FLAVORS + 1> com_coords;
226
+ for (int fl = 1; fl <= NUM_FLAVORS; ++fl) {
227
+ if (counts[fl] > 0) {
228
+ com_coords[fl] = {sum_r[fl] / counts[fl], sum_c[fl] / counts[fl]};
229
+ }
230
+ }
231
+
232
+ double total_manhattan_dist_penalty = 0;
233
+ for (const auto& candy : candies_list) {
234
+ if (counts[candy.flavor] > 1) {
235
+ const auto& com = com_coords[candy.flavor];
236
+ total_manhattan_dist_penalty += std::abs(static_cast<double>(candy.r) - com.first) +
237
+ std::abs(static_cast<double>(candy.c) - com.second);
238
+ }
239
+ }
240
+ return total_manhattan_dist_penalty;
241
+ }
242
+
243
+ double calculate_region_penalty() const {
244
+ if (candies_list.empty()) return 0.0;
245
+ double penalty = 0.0;
246
+ for (const auto& candy : candies_list) {
247
+ if (!G_flavor_active[candy.flavor]) continue;
248
+
249
+ const auto& range = G_target_col_ranges[candy.flavor];
250
+ int min_target_c = range.first;
251
+ int max_target_c = range.second;
252
+
253
+ if (min_target_c > max_target_c) continue;
254
+
255
+ if (candy.c < min_target_c) {
256
+ penalty += (min_target_c - candy.c);
257
+ } else if (candy.c > max_target_c) {
258
+ penalty += (candy.c - max_target_c);
259
+ }
260
+ }
261
+ return penalty;
262
+ }
263
+
264
+ double calculate_edge_bonus() const {
265
+ double bonus_val = 0.0;
266
+ const double PER_CANDY_BONUS_FACTOR = 0.5;
267
+
268
+ for (const auto& candy : candies_list) {
269
+ if (!G_flavor_active[candy.flavor]) continue;
270
+
271
+ const auto& range = G_target_col_ranges[candy.flavor];
272
+ int min_target_c = range.first;
273
+ int max_target_c = range.second;
274
+
275
+ if (min_target_c > max_target_c) continue;
276
+
277
+ bool in_correct_strip = (candy.c >= min_target_c && candy.c <= max_target_c);
278
+
279
+ if (in_correct_strip) {
280
+ if (candy.r == 0 || candy.r == GRID_SIZE - 1) {
281
+ bonus_val += PER_CANDY_BONUS_FACTOR;
282
+ }
283
+ if ((candy.c == 0 && min_target_c == 0) ||
284
+ (candy.c == GRID_SIZE - 1 && max_target_c == GRID_SIZE - 1)) {
285
+ bonus_val += PER_CANDY_BONUS_FACTOR;
286
+ }
287
+ }
288
+ }
289
+ return bonus_val;
290
+ }
291
+
292
+ double evaluate() const {
293
+ if (candies_list.empty() && turn_num_1_indexed == 0) return 0.0;
294
+
295
+ long long sum_sq_comp = calculate_sum_sq_comp_size();
296
+ double dist_penalty_com = calculate_distance_penalty_CoM();
297
+ double region_penalty_val = calculate_region_penalty();
298
+ double edge_bonus_val = calculate_edge_bonus();
299
+
300
+ double current_turn_double = static_cast<double>(turn_num_1_indexed);
301
+
302
+ // Coefficients from Iteration 2 (best scoring), with small tweak to C
303
+ double A_coeff_conn = 15.0 + 1.1 * current_turn_double;
304
+ double B_coeff_com_base = std::max(0.0, 170.0 - 1.7 * current_turn_double);
305
+ // Final iteration tweak for C_coeff_region_penalty_direct:
306
+ double C_coeff_region_penalty_direct = std::max(2.0, 27.0 - 0.17 * current_turn_double);
307
+ double D_coeff_edge_bonus = 5.0 + 0.2 * current_turn_double;
308
+
309
+ return A_coeff_conn * sum_sq_comp
310
+ - B_coeff_com_base * dist_penalty_com
311
+ - C_coeff_region_penalty_direct * region_penalty_val
312
+ + D_coeff_edge_bonus * edge_bonus_val;
313
+ }
314
+ };
315
+
316
+ // Forward declaration
317
+ double eval_lookahead(const GameState& state_after_tilt, int turn_T_of_candy_just_processed, int depth_remaining);
318
+
319
+ char decide_tilt_direction_logic(const GameState& current_gs_after_placement) {
320
+ double best_overall_eval = std::numeric_limits<double>::lowest();
321
+ int best_dir_idx = 0;
322
+
323
+ int turn_T_for_lookahead_base = current_gs_after_placement.turn_num_1_indexed;
324
+
325
+ for (int i = 0; i < NUM_DIRECTIONS; ++i) {
326
+ GameState gs_after_tilt_T = current_gs_after_placement;
327
+ gs_after_tilt_T.apply_tilt(i);
328
+
329
+ double current_tilt_eval_for_dir_i = eval_lookahead(gs_after_tilt_T, turn_T_for_lookahead_base, MAX_LOOKAHEAD_DEPTH);
330
+
331
+ if (current_tilt_eval_for_dir_i > best_overall_eval) {
332
+ best_overall_eval = current_tilt_eval_for_dir_i;
333
+ best_dir_idx = i;
334
+ }
335
+ }
336
+ return DIR_CHARS[best_dir_idx];
337
+ }
338
+
339
+
340
+ double eval_lookahead(const GameState& state_after_tilt, int turn_T_of_candy_just_processed, int depth_remaining) {
341
+ if (depth_remaining == 0 || turn_T_of_candy_just_processed == NUM_TURNS) {
342
+ return state_after_tilt.evaluate();
343
+ }
344
+
345
+ int num_empty = state_after_tilt.count_empty_cells();
346
+ if (num_empty == 0) {
347
+ return state_after_tilt.evaluate();
348
+ }
349
+
350
+ int next_candy_flavor = G_FLAVOR_SEQUENCE[turn_T_of_candy_just_processed];
351
+ int sample_count_param_idx = MAX_LOOKAHEAD_DEPTH - depth_remaining;
352
+ int sample_count_this_depth = NUM_SAMPLES_CONFIG[sample_count_param_idx];
353
+ int actual_num_samples = std::min(sample_count_this_depth, num_empty);
354
+
355
+ if (actual_num_samples == 0) {
356
+ return state_after_tilt.evaluate();
357
+ }
358
+
359
+ double sum_over_sampled_placements = 0.0;
360
+ for (int s = 0; s < actual_num_samples; ++s) {
361
+ int p_val_1_indexed_sample;
362
+ if (actual_num_samples == num_empty) {
363
+ p_val_1_indexed_sample = s + 1;
364
+ } else {
365
+ p_val_1_indexed_sample = rng.uniform_int(1, num_empty);
366
+ }
367
+
368
+ GameState S_after_placement = state_after_tilt;
369
+ std::pair<int, int> candy_loc = S_after_placement.find_pth_empty_cell(p_val_1_indexed_sample);
370
+ S_after_placement.place_candy(candy_loc.first, candy_loc.second, next_candy_flavor);
371
+ S_after_placement.turn_num_1_indexed = turn_T_of_candy_just_processed + 1;
372
+
373
+ double max_eval_for_this_placement = std::numeric_limits<double>::lowest();
374
+ for (int dir_idx_next_tilt = 0; dir_idx_next_tilt < NUM_DIRECTIONS; ++dir_idx_next_tilt) {
375
+ GameState S_after_next_tilt = S_after_placement;
376
+ S_after_next_tilt.apply_tilt(dir_idx_next_tilt);
377
+ double val = eval_lookahead(S_after_next_tilt, S_after_placement.turn_num_1_indexed, depth_remaining - 1);
378
+ if (val > max_eval_for_this_placement) {
379
+ max_eval_for_this_placement = val;
380
+ }
381
+ }
382
+ sum_over_sampled_placements += max_eval_for_this_placement;
383
+ }
384
+
385
+ return sum_over_sampled_placements / actual_num_samples;
386
+ }
387
+
388
+
389
+ void initialize_global_data() {
390
+ G_flavor_total_counts.fill(0);
391
+ for (int t = 0; t < NUM_TURNS; ++t) {
392
+ std::cin >> G_FLAVOR_SEQUENCE[t];
393
+ G_flavor_total_counts[G_FLAVOR_SEQUENCE[t]]++;
394
+ }
395
+
396
+ G_flavor_active.fill(false);
397
+ std::vector<std::pair<int, int>> sorter_flavor_count_id;
398
+ for (int fl = 1; fl <= NUM_FLAVORS; ++fl) {
399
+ if (G_flavor_total_counts[fl] > 0) {
400
+ G_flavor_active[fl] = true;
401
+ sorter_flavor_count_id.push_back({G_flavor_total_counts[fl], fl});
402
+ }
403
+ }
404
+ std::sort(sorter_flavor_count_id.begin(), sorter_flavor_count_id.end(),
405
+ [](const std::pair<int, int>& a, const std::pair<int, int>& b) {
406
+ if (a.first != b.first) {
407
+ return a.first > b.first;
408
+ }
409
+ return a.second < b.second;
410
+ });
411
+
412
+ std::vector<int> active_flavor_ids_sorted_by_priority;
413
+ for(const auto& p : sorter_flavor_count_id) {
414
+ active_flavor_ids_sorted_by_priority.push_back(p.second);
415
+ }
416
+
417
+ std::vector<int> assigned_widths(NUM_FLAVORS + 1, 0);
418
+ int total_assigned_width_sum = 0;
419
+
420
+ if (!active_flavor_ids_sorted_by_priority.empty()) {
421
+ double total_candies_for_proportion = 0;
422
+ for(int fl_id : active_flavor_ids_sorted_by_priority) {
423
+ total_candies_for_proportion += G_flavor_total_counts[fl_id];
424
+ }
425
+ if (total_candies_for_proportion == 0) total_candies_for_proportion = 1;
426
+
427
+ for (int fl_id : active_flavor_ids_sorted_by_priority) {
428
+ assigned_widths[fl_id] = static_cast<int>(std::floor(
429
+ static_cast<double>(GRID_SIZE) * G_flavor_total_counts[fl_id] / total_candies_for_proportion
430
+ ));
431
+ total_assigned_width_sum += assigned_widths[fl_id];
432
+ }
433
+
434
+ int remaining_width_to_assign = GRID_SIZE - total_assigned_width_sum;
435
+ for (int i = 0; i < remaining_width_to_assign; ++i) {
436
+ assigned_widths[active_flavor_ids_sorted_by_priority[i % active_flavor_ids_sorted_by_priority.size()]]++;
437
+ }
438
+ }
439
+
440
+ int current_col_start = 0;
441
+ for (int fl_id_in_sorted_order : active_flavor_ids_sorted_by_priority) {
442
+ if (assigned_widths[fl_id_in_sorted_order] > 0) {
443
+ G_target_col_ranges[fl_id_in_sorted_order] = {current_col_start, current_col_start + assigned_widths[fl_id_in_sorted_order] - 1};
444
+ current_col_start += assigned_widths[fl_id_in_sorted_order];
445
+ } else {
446
+ G_target_col_ranges[fl_id_in_sorted_order] = {current_col_start, current_col_start - 1};
447
+ }
448
+ }
449
+
450
+ for (int fl = 1; fl <= NUM_FLAVORS; ++fl) {
451
+ if (!G_flavor_active[fl]) {
452
+ G_target_col_ranges[fl] = {0, -1};
453
+ }
454
+ }
455
+ }
456
+
457
+
458
+ int main() {
459
+ std::ios_base::sync_with_stdio(false);
460
+ std::cin.tie(NULL);
461
+
462
+ initialize_global_data();
463
+
464
+ GameState current_gs;
465
+ for (int t_0_indexed = 0; t_0_indexed < NUM_TURNS; ++t_0_indexed) {
466
+ current_gs.turn_num_1_indexed = t_0_indexed + 1;
467
+
468
+ int p_val_1_indexed;
469
+ std::cin >> p_val_1_indexed;
470
+
471
+ std::pair<int, int> candy_loc = current_gs.find_pth_empty_cell(p_val_1_indexed);
472
+
473
+ current_gs.place_candy(candy_loc.first, candy_loc.second, G_FLAVOR_SEQUENCE[t_0_indexed]);
474
+
475
+ char chosen_dir_char = decide_tilt_direction_logic(current_gs);
476
+
477
+ std::cout << chosen_dir_char << std::endl;
478
+
479
+ int dir_idx_to_apply = 0;
480
+ for(int k=0; k<NUM_DIRECTIONS; ++k) {
481
+ if(DIR_CHARS[k] == chosen_dir_char) {
482
+ dir_idx_to_apply = k;
483
+ break;
484
+ }
485
+ }
486
+ current_gs.apply_tilt(dir_idx_to_apply);
487
+ }
488
+
489
+ return 0;
490
+ }
491
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc016/best_program.cpp ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <bits/stdc++.h>
3
+ using namespace std;
4
+
5
+ /* Doc: Encode with deterministic block patterns. Signature = sorted expected noisy degrees + expected edge count.
6
+ Select patterns via farthest-first in this signature space; decode by nearest neighbor on the same signature. */
7
+
8
+ /* Squared Euclidean distance between equal-length vectors */
9
+ static inline double sse(const vector<double>& a, const vector<double>& b) {
10
+ double s = 0.0; int n = (int)a.size();
11
+ for (int i = 0; i < n; ++i) { double d = a[i] - b[i]; s += d * d; }
12
+ return s;
13
+ }
14
+
15
+ /* Doc: Return s positive integers that sum to N, approximately geometric with ratio r to separate degree levels. */
16
+ static inline vector<int> build_block_sizes(int N, int s, double r) {
17
+ vector<double> w(s); double sum = 0.0;
18
+ for (int i = 0; i < s; ++i) { w[i] = pow(r, i); sum += w[i]; }
19
+ vector<double> exact(s);
20
+ vector<int> base(s);
21
+ int total = 0;
22
+ for (int i = 0; i < s; ++i) { exact[i] = (double)N * w[i] / sum; base[i] = max(1, (int)floor(exact[i])); total += base[i]; }
23
+ vector<pair<double,int>> frac;
24
+ for (int i = 0; i < s; ++i) frac.emplace_back(exact[i] - floor(exact[i]), i);
25
+ sort(frac.begin(), frac.end(), greater<pair<double,int>>());
26
+ while (total < N) { for (int t = 0; t < s && total < N; ++t) { base[frac[t].second]++; total++; } }
27
+ while (total > N) { for (int i = s - 1; i >= 0 && total > N; --i) if (base[i] > 1) { base[i]--; total--; } }
28
+ return base;
29
+ }
30
+
31
+ /* Doc: Build sorted vector of expected noisy degrees under BSC noise: mu_i = eps*(N-1) + (1-2eps)*deg(block_i). */
32
+ static inline vector<double> build_mu_sorted(const vector<int>& sz, const vector<unsigned int>& mask, const vector<unsigned char>& diag, double eps) {
33
+ int s = (int)sz.size(); int N = 0; for (int v : sz) N += v;
34
+ vector<double> mu; mu.reserve(N);
35
+ const double a = eps * (N - 1), b = 1.0 - 2.0 * eps;
36
+ for (int bidx = 0; bidx < s; ++bidx) {
37
+ int d = diag[bidx] ? (sz[bidx] - 1) : 0;
38
+ unsigned int m = mask[bidx];
39
+ for (int c = 0; c < s; ++c) if (c != bidx && ((m >> c) & 1U)) d += sz[c];
40
+ double mv = a + b * (double)d;
41
+ for (int t = 0; t < sz[bidx]; ++t) mu.push_back(mv);
42
+ }
43
+ sort(mu.begin(), mu.end());
44
+ return mu;
45
+ }
46
+
47
+ /* Doc: Random symmetric block pattern: diag[b] in {0,1}, cross-block bits symmetric for i<j. */
48
+ static inline void gen_random_pattern(int s, mt19937& rng, vector<unsigned int>& mask, vector<unsigned char>& diag) {
49
+ mask.assign(s, 0U); diag.assign(s, 0);
50
+ uniform_int_distribution<int> bit01(0, 1);
51
+ for (int i = 0; i < s; ++i) diag[i] = (unsigned char)bit01(rng);
52
+ for (int i = 0; i < s; ++i) for (int j = i + 1; j < s; ++j) if (bit01(rng)) { mask[i] |= (1U << j); mask[j] |= (1U << i); }
53
+ }
54
+
55
+ /* Doc: Add a handful of structured patterns (empty/full/diag/off/banded/threshold) to diversify the pool. */
56
+ static inline void gen_structured_patterns(int s, vector<vector<unsigned int>>& masks, vector<vector<unsigned char>>& diags) {
57
+ auto add = [&](const vector<unsigned int>& m, const vector<unsigned char>& d){ masks.push_back(m); diags.push_back(d); };
58
+ vector<unsigned int> m(s,0); vector<unsigned char> d0(s,0), d1(s,1);
59
+ // empty, full
60
+ add(m, d0);
61
+ vector<unsigned int> full(s,0);
62
+ for (int i = 0; i < s; ++i) for (int j = 0; j < s; ++j) if (i!=j) full[i] |= (1U<<j);
63
+ add(full, d1);
64
+ // diag-only, off-only
65
+ add(m, d1);
66
+ add(full, d0);
67
+ // banded |i-j|<=t
68
+ for (int t = 0; t < s; ++t) {
69
+ vector<unsigned int> mm(s,0); vector<unsigned char> dd(s, (unsigned char)(t&1));
70
+ for (int i = 0; i < s; ++i) for (int j = 0; j < s; ++j) if (i!=j && abs(i-j)<=t) mm[i] |= (1U<<j);
71
+ add(mm, dd);
72
+ }
73
+ // threshold i+j <= T
74
+ for (int T = 0; T <= 2*(s-1); T += max(1,s/3)) {
75
+ vector<unsigned int> mm(s,0); vector<unsigned char> dd(s, (unsigned char)((T/2)&1));
76
+ for (int i = 0; i < s; ++i) for (int j = i+1; j < s; ++j) if (i + j <= T) { mm[i] |= (1U<<j); mm[j] |= (1U<<i); }
77
+ add(mm, dd);
78
+ }
79
+ }
80
+
81
+ /* Doc: Pick N as small as possible for score while ensuring enough distinct block patterns and noise robustness.
82
+ Start near a noise-dependent baseline, then increase N until capacity ~ 2^{s(s-1)/2 + b2} >= M (or exponent >=30). */
83
+ static inline int chooseN(int M, double eps) {
84
+ auto s_of_N = [](int N){ return min(8, max(4, (int)floor(log2((double)N)) - 1)); };
85
+ // noise-aware baseline with small-N bias at low eps
86
+ int N = (int)llround(10.0 + 100.0 * eps);
87
+ int Nmin = (eps < 0.15 ? 10 : (eps < 0.28 ? 14 : 18));
88
+ N = max(N, Nmin);
89
+ N = min(N, 100);
90
+ double r = max(1.35, min(2.35, 1.5 + 1.5 * eps));
91
+ while (true) {
92
+ int s = s_of_N(N);
93
+ auto sz = build_block_sizes(N, s, r);
94
+ int b2 = 0; for (int v : sz) if (v >= 2) ++b2;
95
+ int expo = s*(s-1)/2 + b2;
96
+ if (expo >= 30 || (1 << expo) >= M) break;
97
+ if (N >= 100) break;
98
+ ++N;
99
+ }
100
+ if (M > 80 && eps > 0.25) N = min(100, N + 2); // stabilize in high-noise/high-M
101
+ return max(8, N);
102
+ }
103
+
104
+ /* Build adjacency bitstring from block pattern and vertex->block mapping */
105
+ static inline string build_graph_bits(int N, const vector<pair<unsigned char,unsigned char>>& pairs,
106
+ const vector<int>& belong, const vector<unsigned int>& mask, const vector<unsigned char>& diag) {
107
+ int L = N * (N - 1) / 2; string s(L, '0');
108
+ for (int p = 0; p < L; ++p) {
109
+ auto ab = pairs[p];
110
+ int bu = belong[ab.first], bv = belong[ab.second];
111
+ bool conn = (bu == bv) ? (diag[bu] != 0) : (((mask[bu] >> bv) & 1U) != 0);
112
+ if (conn) s[p] = '1';
113
+ }
114
+ return s;
115
+ }
116
+
117
+ int main() {
118
+ ios::sync_with_stdio(false);
119
+ cin.tie(nullptr);
120
+
121
+ int M; double eps;
122
+ if (!(cin >> M >> eps)) return 0;
123
+
124
+ // Tune block-size ratio by noise level to widen degree gaps as noise increases
125
+ double BLOCK_R = max(1.35, min(2.35, 1.5 + 1.5 * eps));
126
+
127
+ // Choose N and s (number of blocks)
128
+ int N = chooseN(M, eps);
129
+ int s = min(8, max(4, (int)floor(log2((double)N)) - 1 + (eps < 0.08 ? 1 : 0) - (eps > 0.28 ? 1 : 0)));
130
+ vector<int> sz = build_block_sizes(N, s, BLOCK_R);
131
+
132
+ // Vertex -> block mapping
133
+ vector<int> belong(N, 0);
134
+ for (int i = 0, cur = 0, b = 0; b < s; ++b) for (int t = 0; t < sz[b]; ++t) belong[cur++] = b;
135
+
136
+ // Precompute (i,j) pairs and constants
137
+ int L = N * (N - 1) / 2;
138
+ vector<pair<unsigned char,unsigned char>> pairs;
139
+ pairs.reserve(L);
140
+ for (int i = 0; i < N; ++i) for (int j = i + 1; j < N; ++j)
141
+ pairs.emplace_back((unsigned char)i, (unsigned char)j);
142
+
143
+ // Variance-normalized weights for fused scoring (degrees + edge count)
144
+ double invVarDeg = 1.0 / ((double)(N - 1) * eps * (1.0 - eps) + 1e-12);
145
+ double invVarM = 1.0 / ((double)L * eps * (1.0 - eps) + 1e-12);
146
+ double degW = 1.0 + 0.8 * eps;
147
+
148
+ // Build candidate pool of block patterns (structured + random + complements)
149
+ int seed = 146527 + M * 1000 + (int)llround(eps * 100.0) * 7919;
150
+ mt19937 rng(seed);
151
+ vector<vector<unsigned int>> cand_masks;
152
+ vector<vector<unsigned char>> cand_diags;
153
+
154
+ gen_structured_patterns(s, cand_masks, cand_diags);
155
+
156
+ auto add_comp = [&](const vector<unsigned int>& m, const vector<unsigned char>& d){
157
+ vector<unsigned int> mc(s, 0u); vector<unsigned char> dc(s, 0);
158
+ unsigned int all = (s >= 31 ? 0x7FFFFFFFu : ((1u << s) - 1u));
159
+ for (int i = 0; i < s; ++i) { unsigned int fulli = all & ~(1u << i); mc[i] = fulli ^ m[i]; dc[i] = (unsigned char)(1 - d[i]); }
160
+ cand_masks.push_back(mc); cand_diags.push_back(dc);
161
+ };
162
+
163
+ int RAND_CANDS = (eps <= 0.12 ? max(6 * M, 384) : min(3072, max(10 * M, 512)));
164
+ for (int t = 0; t < RAND_CANDS; ++t) {
165
+ vector<unsigned int> m; vector<unsigned char> d;
166
+ gen_random_pattern(s, rng, m, d);
167
+ cand_masks.push_back(m); cand_diags.push_back(d);
168
+ add_comp(m, d);
169
+ }
170
+
171
+ struct Cand { vector<unsigned int> mask; vector<unsigned char> diag; vector<double> mu; double mu_m; };
172
+ vector<Cand> pool; pool.reserve(cand_masks.size());
173
+ for (size_t i = 0; i < cand_masks.size(); ++i) {
174
+ vector<double> mu = build_mu_sorted(sz, cand_masks[i], cand_diags[i], eps);
175
+ long long edges = 0;
176
+ for (int b = 0; b < s; ++b) if (cand_diags[i][b]) edges += 1LL * sz[b] * (sz[b] - 1) / 2;
177
+ for (int b = 0; b < s; ++b) for (int c = b + 1; c < s; ++c)
178
+ if ((cand_masks[i][b] >> c) & 1U) edges += 1LL * sz[b] * sz[c];
179
+ double mu_m = eps * (double)L + (1.0 - 2.0 * eps) * (double)edges;
180
+ pool.push_back({cand_masks[i], cand_diags[i], move(mu), mu_m});
181
+ }
182
+
183
+ // Greedy farthest-point sampling using fused metric on (mu_sorted, mu_m)
184
+ auto fused_dist = [&](int a, int b)->double{
185
+ double d = sse(pool[a].mu, pool[b].mu) * invVarDeg * degW;
186
+ double dm = pool[a].mu_m - pool[b].mu_m;
187
+ return d + dm * dm * invVarM;
188
+ };
189
+ vector<int> chosen;
190
+ int C = (int)pool.size();
191
+ int seedIdx = 0; double bestVar = -1.0;
192
+ for (int i = 0; i < C; ++i) {
193
+ const auto& mu = pool[i].mu;
194
+ double mean = 0.0; for (double v : mu) mean += v; mean /= mu.size();
195
+ double var = 0.0; for (double v : mu) { double d = v - mean; var += d * d; }
196
+ if (var > bestVar) { bestVar = var; seedIdx = i; }
197
+ }
198
+ chosen.push_back(seedIdx);
199
+ for (int k = 1; k < M; ++k) {
200
+ int nxt = -1; double far = -1.0;
201
+ for (int i = 0; i < C; ++i) {
202
+ bool used = false; for (int id : chosen) if (id == i) { used = true; break; }
203
+ if (used) continue;
204
+ double md = 1e300;
205
+ for (int id : chosen) md = min(md, fused_dist(i, id));
206
+ if (md > far) { far = md; nxt = i; }
207
+ }
208
+ if (nxt < 0) nxt = chosen.back();
209
+ chosen.push_back(nxt);
210
+ }
211
+
212
+ // Output graphs
213
+ cout << N << '\n';
214
+ vector<vector<double>> mu_sorted_vec(M, vector<double>(N, 0.0));
215
+ vector<double> mu_m_vec(M, 0.0);
216
+ for (int k = 0; k < M; ++k) {
217
+ const auto& pat = pool[chosen[k]];
218
+ mu_sorted_vec[k] = pat.mu; mu_m_vec[k] = pat.mu_m;
219
+ string sbits = build_graph_bits(N, pairs, belong, pat.mask, pat.diag);
220
+ cout << sbits << '\n';
221
+ }
222
+ cout.flush();
223
+
224
+ // Online decoding: fused nearest neighbor using degree multiset + edge count
225
+ for (int q = 0; q < 100; ++q) {
226
+ string H; if (!(cin >> H)) return 0;
227
+ vector<int> deg(N, 0); int m = 0;
228
+ for (int p = 0; p < L; ++p) if (H[p] == '1') { auto ab = pairs[p]; deg[(int)ab.first]++; deg[(int)ab.second]++; ++m; }
229
+ vector<double> dh(N); for (int i = 0; i < N; ++i) dh[i] = (double)deg[i];
230
+ sort(dh.begin(), dh.end());
231
+
232
+ int best = 0; double bestS = 1e300;
233
+ for (int k = 0; k < M; ++k) {
234
+ double sdeg = sse(dh, mu_sorted_vec[k]) * invVarDeg * degW;
235
+ double dm = (double)m - mu_m_vec[k];
236
+ double score = sdeg + dm * dm * invVarM;
237
+ if (score < bestS) { bestS = score; best = k; }
238
+ }
239
+ cout << best << '\n';
240
+ cout.flush();
241
+ }
242
+ return 0;
243
+ }
244
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc016/config.yaml ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ALE-Bench ahc016 — AtCoder Heuristic Contest
2
+ # Usage: skydiscover-run initial_program.cpp evaluator.py -c config.yaml -s <strategy>
3
+ language: cpp
4
+ diff_based_generation: true
5
+ max_iterations: 100
6
+ checkpoint_interval: 10
7
+ max_solution_length: 60000
8
+ llm:
9
+ api_base: https://api.openai.com/v1
10
+ models:
11
+ - name: "gpt-5"
12
+ weight: 1.0
13
+ max_tokens: 32000
14
+ timeout: 600
15
+ prompt:
16
+ system_message: "You are a world-class algorithm engineer, and you are very good at programming. Now, you are participating\
17
+ \ in a programming contest. You are asked to solve a heuristic problem, known as an NP-hard problem.\n \n Story\n\
18
+ --------\nTakahashi, a genius inventor, invented the time machine called \"Graphorean\" (Graph + DeLorean) that can send\
19
+ \ a <a href=\"https://en.wikipedia.org/wiki/Graph_(abstract_data_type)\">graph</a> to the past.\nWith this machine, he\
20
+ \ plans to get rich by playing casino roulette and sending the winning number information to the time before he plays.\n\
21
+ If he succeeds, he will move to the world line where he has successfully chosen the winning number and become very rich.\n\
22
+ \nBecause the machine cannot send the winning number directly, he needs to first convert the number into a graph (encoding),\
23
+ \ then send it, and finally convert the graph back into a number (decoding).\nSending a graph by the machine loses the\
24
+ \ vertex number information and introduces noise, so he must develop encoding/decoding methods so that the number can\
25
+ \ be correctly restored.\nIn order to receive a graph with $N$ vertices, he must set an integer $N$ to the machine.\n\
26
+ Therefore, the number of vertices of the graph to be sent must be determined in advance.\n\nThe time machine will be broken\
27
+ \ once he uses it, so failure will not be tolerated.\nTherefore, he decided to estimate the success probability by conducting\
28
+ \ simulations in advance and preparing an encoding/decoding method with as high a success probability as possible.\nFurthermore,\
29
+ \ because sending a large graph requires a huge amount of energy, it is desirable that the graph's size be as small as\
30
+ \ possible.\nPlease help him.\n\n\nProblem Statement\n--------\nGiven an integer $M$ and an error rate $\\epsilon$, determine\
31
+ \ an integer $N$ satisfying $4\\leq N\\leq 100$ and output $N$-vertex undirected graphs $G_0,G_1,\\cdots,G_{M-1}$.\nThe\
32
+ \ graphs may be disconnected.\nThen process the following query $100$ times.\n\nIn the $k$-th query, you are given an\
33
+ \ $N$-vertex undirected graph $H_k$.\n$H_k$ is a graph generated from some $G_{s_k}$ as follows.\n\n1. Initialize $H_k=G_{s_k}$.\n\
34
+ 2. For each $(i,j)$ with $0\\leq i<j\\leq N-1$, flip whether or not $H_k$ contains edge $(i,j)$ with probability $\\epsilon$.\n\
35
+ 3. Randomly shuffle the order of the vertices in $H_k$.\n\nAfter receiving $H_k$, predict from which graph $G_{s_k}$ it\
36
+ \ was generated, and output the predicted value $t_k$ of $s_k$.\n\n\nScoring\n--------\nLet $E$ be the number of failed\
37
+ \ predictions.\nThen the score for the test case is\n\n\\\\[\n\t\\mathrm{round}\\left(10^9\\times \\frac{0.9^E}{N}\\right)\n\
38
+ \\\\]\n\n\n\n#### Number of test cases\n- Provisional test: 50\n- System test: 2000. We will publish <a href=\"https://img.atcoder.jp/ahc016/seeds.txt\"\
39
+ >seeds.txt</a> (sha256=4093b6cb740beea16eb0ecf55120ca6ca6fbef18015ea4a863e64d0bea3de91d) after the contest is over.\n\
40
+ - System test contains at most one test case for each pair of $(M,\\epsilon)$.\n\nFor each test case, we compute the <font\
41
+ \ color=\"red\"><strong>relative score</strong></font> $\\mathrm{round}(10^9\\times \\frac{\\mathrm{YOUR}}{\\mathrm{MAX}})$,\
42
+ \ where YOUR is your score and MAX is the highest score among all competitors obtained on that test case.\nThe score of\
43
+ \ the submission is the sum of the relative scores.\n\nThe final ranking will be determined by the system test with more\
44
+ \ inputs which will be run after the contest is over.\nIn both the provisional/system test, if your submission produces\
45
+ \ illegal output or exceeds the time limit for some test cases, only the score for those test cases will be zero.\nThe\
46
+ \ system test will be performed only for <font color=\"red\"><strong>the last submission which received a result other\
47
+ \ than <span class=\"label label-warning\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"\
48
+ Compilation Error\">CE</span> </strong></font>.\nBe careful not to make a mistake in the final submission.\n\n\n\n####\
49
+ \ About Relative Evaluation System\nIn both the provisional/system test, the standings will be calculated using only the\
50
+ \ last submission which received a result other than <span class=\"label label-warning\" data-toggle=\"tooltip\" data-placement=\"\
51
+ top\" title=\"\" data-original-title=\"Compilation Error\">CE</span>.\nOnly the last submissions are used to calculate\
52
+ \ the highest score among all competitors for each test case in calculating the relative scores.\n\nThe scores shown in\
53
+ \ the standings are relative, and whenever a new submission arrives, all relative scores are recalculated.\nOn the other\
54
+ \ hand, the score for each submission shown on the submissions page is an absolute score obtained by summing up the scores\
55
+ \ for each test case, and the relative scores are not shown.\nIn order to know the relative score of submission other\
56
+ \ than the latest one in the current standings, you need to resubmit it.\n<strong>(Update)</strong> If your submission\
57
+ \ produces illegal output or exceeds the time limit for some test cases, the absolute score shown in the submissions page\
58
+ \ becomes 0, but the standings show the sum of the relative scores for the test cases that were answered correctly.\n\n\
59
+ #### About execution time\nExecution time may vary slightly from run to run.\nIn addition, since system tests simultaneously\
60
+ \ perform a large number of executions, it has been observed that execution time increases by several percent compared\
61
+ \ to provisional tests.\nFor these reasons, submissions that are very close to the time limit may result in <span class='label\
62
+ \ label-warning' data-toggle='tooltip' data-placement='top' title=\"Time Limit Exceeded\">TLE</span> in the system test.\n\
63
+ Please measure the execution time in your program to terminate the process, or have enough margin in the execution time.\n\
64
+ \n\n\nInput and Output\n--------\nFirst, information about the problem setting is given from Standard Input in the following\
65
+ \ format.\n~~~\n$M$ $\\epsilon$\n~~~\n- $M$ is an integer representing the number of graphs, satisfying $10\\leq M\\leq\
66
+ \ 100$.\n- $\\epsilon$ is a real number representing the error rate, which is a multiple of $0.01$ and satisfies $0.00\\\
67
+ leq \\epsilon\\leq 0.4$.\n\nAfter reading the input, output $M$ graphs $G_0,G_1,\\cdots,G_{M-1}$ to Standard Output in\
68
+ \ the following format.\n~~~\n$N$\n$g_0$\n$\\vdots$\n$g_{M-1}$\n~~~\n- $N$ is an integer representing the number of vertices\
69
+ \ in each graph, which must satisfy $4\\leq N\\leq 100$.\n- Each $g_k$ is a string of length $N(N-1)/2$, which represents\
70
+ \ the $k$-th graph $G_k$ as follows. For each $(i,j)$ satisfying $0\\leq i<j\\leq N-1$, express the existence of edge\
71
+ \ $(i,j)$ as `1` if $G_k$ contains edge $(i,j)$ and `0` if it does not, using one character, and then arrange them in\
72
+ \ lexicographic order of $(i,j)$. For example, when $N=4$, the string `100101` represents a graph with $4$ vertices connected\
73
+ \ on a straight line.\n\n<font color=\"red\">**After output, you have to flush Standard Output.**</font>\nOtherwise, the\
74
+ \ submission might be judged as <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"\
75
+ Time Limit Exceeded\">TLE</span>.\nAfter outputting $M$ graphs, repeat the following process $100$ times.\n\nIn the $k$-th\
76
+ \ process, you are given an $N$-vertex graph $H_k$ represented as a string of $N(N-1)/2$ `01` characters in the same format\
77
+ \ as above, in a single line from Standard Input.\nAfter receiving $H_k$, predict from which graph $G_{s_k}$ $H_k$ is\
78
+ \ generated and output the prediction $t_k (0\\leq t_k\\leq M-1)$ of $s_k$ to Standard Output.\nThe output must be followed\
79
+ \ by a new line, and you have to flush Standard Output.\n<font color=\"red\">**Note that $H_{k+1}$ will not be given until\
80
+ \ you output the $t_k$.**</font>\n\nSample Solution\n--------\nThis is a sample solution in Python.\nIn this program,\
81
+ \ we set $N=20$, and each graph $G_k$ contains $k$ edges.\nFor each $H_k$, we count the number of edges $m$ and output\
82
+ \ $\\min(m, M-1)$.\n\n<pre class=\"prettyprint linenums\">M, eps = input().split()\nM = int(M)\neps = float(eps)\nprint(20)\n\
83
+ for k in range(M):\n\tprint(\"1\" * k + \"0\" * (190 - k))\n\nfor q in range(100):\n\tH = input()\n\tt = min(H.count('1'),\
84
+ \ M - 1)\n\tprint(t)\n</pre>\n\n\nInput Generation\n--------\nLet $\\mathrm{rand}(L,U)$ be a function that generates a\
85
+ \ uniform random integer between $L$ and $U$, inclusive.\n$M$ is generated by $\\mathrm{rand}(10,100)$.\n$\\epsilon$ is\
86
+ \ generated by $0.01\\times \\mathrm{rand}(0,40)$.\nEach $s_k$ is generated by $\\mathrm{rand}(0,M-1)$.\n\n\nTools (Input\
87
+ \ generator and visualizer)\n--------\n- <a href=\"https://img.atcoder.jp/ahc016/d5f3c281.html?lang=en\">Web version</a>:\
88
+ \ You can see the visualization of each input/output graph.\n- <a href=\"https://img.atcoder.jp/ahc016/d5f3c281.zip\"\
89
+ >Local version</a>: You need a compilation environment of <a href=\"https://www.rust-lang.org/\">Rust language</a>.\n\t\
90
+ - <a href=\"https://img.atcoder.jp/ahc016/d5f3c281_windows.zip\">Pre-compiled binary for Windows</a>: If you are not familiar\
91
+ \ with the Rust language environment, please use this instead.\n\n<font color=\"red\">**Please be aware that sharing visualization\
92
+ \ results or discussing solutions/ideas during the contest is prohibited.**</font>\n\n#### Specification of input/output\
93
+ \ files used by the tools\nInput files given to the local tester have the following format.\n~~~\n$M$ $\\epsilon$\n$s_0$\n\
94
+ $\\vdots$\n$s_{99}$\n$\\mathrm{seed}$\n~~~\nThe last $\\mathrm{seed}$ is a random seed value used for noise generation.\n\
95
+ Since each graph $H_0,\\cdots,H_{99}$ depends on output graphs $G_0,\\cdots,G_{M-1}$, the input file contains only the\
96
+ \ random seed value.\n\nThe local tester writes outputs from your program directly to the output file.\nYour program may\
97
+ \ output comment lines starting with `#`.\nThe web version of the visualizer displays the comment lines with the corresponding\
98
+ \ query, which may be useful for debugging and analysis.\nSince the judge program ignores all comment lines, you can submit\
99
+ \ a program that outputs comment lines as is.\n\nComment lines that begin with the following have special meaning in the\
100
+ \ visualizer.\n\n- `#v`: You can tell the visualizer that you have predicted that vertex $i$ in $H_k$ corresponds to vertex\
101
+ \ $p_i$ in $G_{t_k}$ by outputting it in the following format.\n~~~\n#v $p_0$ $\\cdots$ $p_{N-1}$\n~~~\n- `#h`: If you\
102
+ \ do not use the provided local tester, you can replace the $H_k$ displayed by the visualizer by outputting in the form\
103
+ \ `#h 100101 001101`. The left is a graph after adding noise to $G_{s_k}$, and the right is a graph after shuffling the\
104
+ \ vertex ordering.\n\n\n Problem constraints:\n time_limit=5.0 memory_limit=1073741824\n"
105
+ evaluator:
106
+ timeout: 10000
107
+ cascade_evaluation: false
108
+
benchmarks/ale_bench/ale-bench-lite-problems/ahc016/evaluator.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+ from pathlib import Path
3
+ from ale_bench.result import CaseResult, JudgeResult, Result
4
+ from ale_bench_eval.safe_ale_session import start_ale_bench_session
5
+ import logging
6
+ import sys
7
+ logger = logging.getLogger(__name__ + "_" + "ALE_BENCH_EVALUATOR")
8
+
9
+ def result_feedback(result: Result) -> CaseResult:
10
+ if result.overall_judge_result == JudgeResult.ACCEPTED:
11
+ return result.case_results[0]
12
+ else:
13
+ selected_case_idx = 0
14
+ for idx, case_result in enumerate(result.case_results):
15
+ if case_result.judge_result == result.overall_judge_result:
16
+ selected_case_idx = idx
17
+ break
18
+ return result.case_results[selected_case_idx]
19
+
20
+ def evaluate(program_path):
21
+ problem_id = "ahc016"
22
+ logger.info(f"Evaluating program {program_path} for problem {problem_id} in ale bench evaluator")
23
+ try:
24
+ session = None
25
+ logger.info("Starting ALE-Bench session")
26
+ session = start_ale_bench_session(
27
+ problem_id=problem_id,
28
+ lite_version=True,
29
+ num_workers=13,
30
+ )
31
+ logger.info("ALE-Bench session started")
32
+ if not session:
33
+ raise RuntimeError("Failed to start or restart the session.")
34
+ optim_factor = 1 if session.problem.metadata.score_type == "maximize" else -1
35
+ code = Path(program_path).read_text().replace("# EVOLVE-BLOCK-START", "").replace("# EVOLVE-BLOCK-END", "").strip()
36
+ logger.info("Code extracted")
37
+ num_public_cases = 50
38
+ cases = session.case_gen(list(range(num_public_cases)))
39
+ public_result = session.case_eval(
40
+ cases, code, code_language="cpp20", skip_local_visualization=True
41
+ )
42
+ logger.info("Public evaluation completed")
43
+ extracted_case = result_feedback(public_result)
44
+ logger.info("Result feedback completed")
45
+ logger.info("ALE-Bench session closed")
46
+ combined_score = public_result.overall_absolute_score * optim_factor / num_public_cases
47
+ if public_result.overall_judge_result != JudgeResult.ACCEPTED and optim_factor == -1:
48
+ combined_score = -sys.maxsize - 1
49
+ session.close()
50
+ return {
51
+ "judge_result": public_result.overall_judge_result.value,
52
+ "overall_score": public_result.overall_absolute_score,
53
+ "max_execution_time_sec": max([case_result.execution_time for case_result in public_result.case_results]),
54
+ "max_memory_usage_mib": max([case_result.memory_usage for case_result in public_result.case_results]) // 1024 // 1024,
55
+ "standard_error": extracted_case.error_str,
56
+ "message": extracted_case.message,
57
+ "combined_score": combined_score,
58
+ }
59
+ except Exception as e:
60
+ logger.error(f"Evaluation failed completely: {str(e)}")
61
+ logger.error(traceback.format_exc())
62
+ return {
63
+ "overall_score": 0.0,
64
+ "error": str(e),
65
+ }
benchmarks/ale_bench/ale-bench-lite-problems/ahc016/initial_program.cpp ADDED
@@ -0,0 +1,495 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #ifndef ONLINE_JUDGE
3
+ // #define DEBUG_OUTPUT // Uncomment for local debug prints
4
+ #endif
5
+
6
+ #include <iostream>
7
+ #include <vector>
8
+ #include <string>
9
+ #include <numeric>
10
+ #include <algorithm>
11
+ #include <random>
12
+ #include <set>
13
+ #include <array>
14
+ #include <iomanip>
15
+ #include <cmath>
16
+ #include <chrono>
17
+ #include <map>
18
+
19
+ // Max N for which we attempt full GED based strategy.
20
+ constexpr int N_MAX_GED_CAP = 6;
21
+
22
+ // Adjacency matrix for H_k received in query, or for G_i during pairwise GED. Max N=100
23
+ bool CURRENT_GRAPH_ADJ_QUERY[100][100];
24
+
25
+ int N_ACTUAL;
26
+ int L_ACTUAL; // N_ACTUAL * (N_ACTUAL - 1) / 2
27
+
28
+ // Stores chosen G_j graphs as adjacency matrices (for GED strategy, N <= N_MAX_GED_CAP)
29
+ std::vector<std::array<std::array<bool, N_MAX_GED_CAP>, N_MAX_GED_CAP>> G_ADJS_CHOSEN_GED;
30
+
31
+ // For large N strategy (edge density)
32
+ std::vector<std::string> G_STRINGS_CHOSEN_LARGE_N;
33
+ std::vector<int> G_EDGE_COUNTS_LARGE_N;
34
+
35
+ std::vector<int> P_VERTS_PERM_QUERY; // Permutation vector for GED in query
36
+ std::mt19937 RND_ENGINE;
37
+
38
+ // Temp storage for canonical mask generation (N <= N_MAX_GED_CAP)
39
+ bool CANON_TMP_ADJ[N_MAX_GED_CAP][N_MAX_GED_CAP];
40
+ std::vector<int> CANON_P_PERM;
41
+
42
+ enum class Strategy {
43
+ GED,
44
+ EDGE_COUNT
45
+ };
46
+ Strategy current_strategy;
47
+
48
+ const std::vector<uint16_t> PRECOMPUTED_CANONICAL_MASKS_N6 = {
49
+ 0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 27, 29, 31, 37, 39, 43, 45, 47, 53, 55, 61,
50
+ 63, 73, 75, 77, 79, 91, 93, 95, 111, 117, 119, 125, 127, 141, 143, 157, 159, 173, 175,
51
+ 181, 183, 189, 191, 205, 207, 221, 223, 237, 239, 253, 255, 285, 287, 315, 317, 319,
52
+ 349, 351, 379, 381, 383, 413, 415, 445, 447, 477, 479, 509, 511, 565, 567, 573, 575,
53
+ 589, 591, 605, 607, 637, 639, 701, 703, 717, 719, 733, 735, 749, 751, 765, 767, 797,
54
+ 799, 829, 831, 861, 863, 893, 895, 957, 959, 989, 991, 1021, 1023, 1149, 1151, 1213,
55
+ 1215, 1245, 1247, 1277, 1279, 1533, 1535, 1661, 1663, 1789, 1791, 1917, 1919, 2045,
56
+ 2047, 2109, 2111, 2141, 2143, 2173, 2175, 2205, 2207, 2237, 2239, 2269, 2271, 2301,
57
+ 2303, 2685, 2687, 2813, 2815, 2941, 2943, 3069, 3071, 3277, 3279, 3285, 3287, 3293,
58
+ 3295, 3309, 3311, 3325, 3327, 3357, 3359, 3389, 3391, 3421, 3423, 3453, 3455, 3517,
59
+ 3519, 3549, 3551, 3581, 3583, 3613, 3615, 3645, 3647, 3709, 3711, 3773, 3775, 3837,
60
+ 3839, 4095, 8191, 16383, 32767
61
+ }; // Total 156 graphs for N=6.
62
+
63
+
64
+ void mask_to_adj_matrix_small_N(uint16_t mask, int N_nodes, bool adj_matrix[][N_MAX_GED_CAP]) {
65
+ int bit_idx = 0;
66
+ for (int i = 0; i < N_nodes; ++i) {
67
+ adj_matrix[i][i] = false;
68
+ for (int j = i + 1; j < N_nodes; ++j) {
69
+ adj_matrix[i][j] = adj_matrix[j][i] = ((mask >> bit_idx) & 1);
70
+ bit_idx++;
71
+ }
72
+ }
73
+ }
74
+
75
+ uint16_t adj_matrix_to_mask_small_N(int N_nodes, const bool adj_matrix[][N_MAX_GED_CAP], const std::vector<int>& p_perm) {
76
+ uint16_t mask = 0;
77
+ int bit_idx = 0;
78
+ for (int i = 0; i < N_nodes; ++i) {
79
+ for (int j = i + 1; j < N_nodes; ++j) {
80
+ if (adj_matrix[p_perm[i]][p_perm[j]]) {
81
+ mask |= (1U << bit_idx);
82
+ }
83
+ bit_idx++;
84
+ }
85
+ }
86
+ return mask;
87
+ }
88
+
89
+ uint16_t get_canonical_mask(uint16_t mask_val) {
90
+ int current_L_for_canon = N_ACTUAL * (N_ACTUAL - 1) / 2;
91
+ if (current_L_for_canon == 0) return 0;
92
+
93
+ mask_to_adj_matrix_small_N(mask_val, N_ACTUAL, CANON_TMP_ADJ);
94
+
95
+ std::iota(CANON_P_PERM.begin(), CANON_P_PERM.end(), 0);
96
+ uint16_t min_mask_representation = adj_matrix_to_mask_small_N(N_ACTUAL, CANON_TMP_ADJ, CANON_P_PERM);
97
+
98
+ while (std::next_permutation(CANON_P_PERM.begin(), CANON_P_PERM.end())) {
99
+ uint16_t current_perm_mask = adj_matrix_to_mask_small_N(N_ACTUAL, CANON_TMP_ADJ, CANON_P_PERM);
100
+ min_mask_representation = std::min(min_mask_representation, current_perm_mask);
101
+ }
102
+ return min_mask_representation;
103
+ }
104
+
105
+ int calculate_edit_distance_one_perm_small_N(
106
+ const std::array<std::array<bool, N_MAX_GED_CAP>, N_MAX_GED_CAP>& g_j_adj_template
107
+ ) {
108
+ int diff_count = 0;
109
+ for (int i = 0; i < N_ACTUAL; ++i) {
110
+ for (int j = i + 1; j < N_ACTUAL; ++j) {
111
+ bool template_has_edge = g_j_adj_template[i][j];
112
+ bool current_Hk_has_edge = CURRENT_GRAPH_ADJ_QUERY[P_VERTS_PERM_QUERY[i]][P_VERTS_PERM_QUERY[j]];
113
+ if (current_Hk_has_edge != template_has_edge) {
114
+ diff_count++;
115
+ }
116
+ }
117
+ }
118
+ return diff_count;
119
+ }
120
+
121
+ int min_edit_distance_global_perm_small_N(
122
+ const std::array<std::array<bool, N_MAX_GED_CAP>, N_MAX_GED_CAP>& g_j_adj_template
123
+ ) {
124
+ if (L_ACTUAL == 0) return 0;
125
+
126
+ std::iota(P_VERTS_PERM_QUERY.begin(), P_VERTS_PERM_QUERY.end(), 0);
127
+ int min_dist = L_ACTUAL + 1;
128
+
129
+ long long N_factorial = 1;
130
+ for(int i=1; i<=N_ACTUAL; ++i) N_factorial *= i;
131
+
132
+ long long ops_count = 0;
133
+ do {
134
+ int current_dist = calculate_edit_distance_one_perm_small_N(g_j_adj_template);
135
+ min_dist = std::min(min_dist, current_dist);
136
+ if (min_dist == 0) break;
137
+
138
+ ops_count++;
139
+ if (ops_count >= N_factorial) break;
140
+ } while (std::next_permutation(P_VERTS_PERM_QUERY.begin(), P_VERTS_PERM_QUERY.end()));
141
+
142
+ return min_dist;
143
+ }
144
+
145
+
146
+ std::vector<uint16_t> available_canonical_masks;
147
+ std::vector<std::vector<int>> all_pairwise_ged_cache;
148
+ std::map<uint16_t, int> mask_to_idx_map;
149
+ std::vector<int> chosen_mask_indices_greedy;
150
+
151
+ std::string generate_random_graph_string_large_n(int num_edges, int current_L) {
152
+ std::string s_out(current_L, '0');
153
+ if (num_edges <= 0 || current_L == 0) return s_out;
154
+ if (num_edges >= current_L) {
155
+ std::fill(s_out.begin(), s_out.end(), '1');
156
+ return s_out;
157
+ }
158
+ std::vector<int> edge_indices(current_L);
159
+ std::iota(edge_indices.begin(), edge_indices.end(), 0);
160
+ std::shuffle(edge_indices.begin(), edge_indices.end(), RND_ENGINE);
161
+ for (int i = 0; i < num_edges; ++i) {
162
+ s_out[edge_indices[i]] = '1';
163
+ }
164
+ return s_out;
165
+ }
166
+
167
+ int count_set_bits_in_string(const std::string& s) {
168
+ return std::count(s.begin(), s.end(), '1');
169
+ }
170
+
171
+ void string_to_adj_matrix_query(const std::string& s, int N_nodes) {
172
+ int char_idx = 0;
173
+ for(int i=0; i<N_nodes; ++i) {
174
+ CURRENT_GRAPH_ADJ_QUERY[i][i] = false;
175
+ for(int j=i+1; j<N_nodes; ++j) {
176
+ if (char_idx < (int)s.length()) {
177
+ CURRENT_GRAPH_ADJ_QUERY[i][j] = CURRENT_GRAPH_ADJ_QUERY[j][i] = (s[char_idx++] == '1');
178
+ } else {
179
+ CURRENT_GRAPH_ADJ_QUERY[i][j] = CURRENT_GRAPH_ADJ_QUERY[j][i] = false;
180
+ }
181
+ }
182
+ }
183
+ }
184
+
185
+
186
+ int main() {
187
+ std::ios_base::sync_with_stdio(false);
188
+ std::cin.tie(NULL);
189
+
190
+ unsigned int seed_val = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
191
+ RND_ENGINE.seed(seed_val);
192
+
193
+ int M_graphs;
194
+ double epsilon_noise_rate;
195
+ std::cin >> M_graphs >> epsilon_noise_rate;
196
+
197
+ int N_for_GED_strat;
198
+ if (M_graphs <= 11) N_for_GED_strat = 4;
199
+ else if (M_graphs <= 34) N_for_GED_strat = 5;
200
+ else N_for_GED_strat = N_MAX_GED_CAP;
201
+
202
+ const double K_SEP = 2.5;
203
+ double L_ideal;
204
+ double L_ideal_numerator = K_SEP * K_SEP * (M_graphs > 1 ? (M_graphs - 1.0) * (M_graphs - 1.0) : 1.0) *
205
+ epsilon_noise_rate * (1.0 - epsilon_noise_rate);
206
+ double L_ideal_denominator_factor = (0.5 - epsilon_noise_rate);
207
+ double L_ideal_denominator = L_ideal_denominator_factor * L_ideal_denominator_factor;
208
+
209
+ if (std::abs(0.5 - epsilon_noise_rate) < 1e-9) {
210
+ L_ideal = (100.0 * 99.0) / 2.0;
211
+ } else {
212
+ L_ideal = L_ideal_numerator / L_ideal_denominator;
213
+ }
214
+ if (L_ideal < 0) L_ideal = 0;
215
+
216
+ int N_candidate_EC = 4;
217
+ if (L_ideal > 1e-9) {
218
+ double discriminant = 1.0 + 8.0 * L_ideal;
219
+ if (discriminant >=0) {
220
+ N_candidate_EC = static_cast<int>(std::ceil((1.0 + std::sqrt(discriminant)) / 2.0));
221
+ } else {
222
+ N_candidate_EC = 100;
223
+ }
224
+ }
225
+ N_candidate_EC = std::max(4, N_candidate_EC);
226
+ N_candidate_EC = std::min(100, N_candidate_EC);
227
+
228
+ if (epsilon_noise_rate < 0.01) {
229
+ current_strategy = Strategy::GED; N_ACTUAL = N_for_GED_strat;
230
+ } else {
231
+ if (N_candidate_EC > N_for_GED_strat) {
232
+ current_strategy = Strategy::EDGE_COUNT; N_ACTUAL = N_candidate_EC;
233
+ } else {
234
+ current_strategy = Strategy::GED; N_ACTUAL = N_for_GED_strat;
235
+ }
236
+ }
237
+ N_ACTUAL = std::min(100, std::max(4, N_ACTUAL)); // Final check on N_ACTUAL bounds
238
+
239
+ L_ACTUAL = N_ACTUAL * (N_ACTUAL - 1) / 2;
240
+ std::cout << N_ACTUAL << std::endl;
241
+
242
+ #ifdef DEBUG_OUTPUT
243
+ std::cerr << "# M=" << M_graphs << ", eps=" << epsilon_noise_rate << std::endl;
244
+ std::cerr << "# Chosen N=" << N_ACTUAL << ", Strategy=" << (current_strategy == Strategy::GED ? "GED" : "EDGE_COUNT") << std::endl;
245
+ std::cerr << "# L_ideal=" << L_ideal << ", N_candidate_EC=" << N_candidate_EC << ", N_for_GED_strat=" << N_for_GED_strat << std::endl;
246
+ #endif
247
+
248
+ if (current_strategy == Strategy::GED) {
249
+ P_VERTS_PERM_QUERY.resize(N_ACTUAL); CANON_P_PERM.resize(N_ACTUAL);
250
+
251
+ if (N_ACTUAL == 6) {
252
+ available_canonical_masks = PRECOMPUTED_CANONICAL_MASKS_N6;
253
+ } else {
254
+ std::set<uint16_t> unique_masks_set;
255
+ if (L_ACTUAL > 0) {
256
+ for (unsigned int i = 0; i < (1U << L_ACTUAL); ++i) {
257
+ unique_masks_set.insert(get_canonical_mask(static_cast<uint16_t>(i)));
258
+ }
259
+ } else {
260
+ unique_masks_set.insert(0);
261
+ }
262
+ available_canonical_masks.assign(unique_masks_set.begin(), unique_masks_set.end());
263
+ }
264
+
265
+ int num_total_isos = available_canonical_masks.size();
266
+ #ifdef DEBUG_OUTPUT
267
+ std::cerr << "# Num non-isomorphic graphs for N=" << N_ACTUAL << " is " << num_total_isos << std::endl;
268
+ #endif
269
+ mask_to_idx_map.clear();
270
+ for(int i=0; i<num_total_isos; ++i) mask_to_idx_map[available_canonical_masks[i]] = i;
271
+
272
+ if (num_total_isos > 0) {
273
+ all_pairwise_ged_cache.assign(num_total_isos, std::vector<int>(num_total_isos, 0));
274
+ bool graph_i_adj_cstyle[N_MAX_GED_CAP][N_MAX_GED_CAP];
275
+ std::array<std::array<bool, N_MAX_GED_CAP>, N_MAX_GED_CAP> graph_j_adj_stdarray;
276
+
277
+ for (int i = 0; i < num_total_isos; ++i) {
278
+ mask_to_adj_matrix_small_N(available_canonical_masks[i], N_ACTUAL, graph_i_adj_cstyle);
279
+ for(int r=0; r<N_ACTUAL; ++r) for(int c=0; c<N_ACTUAL; ++c) CURRENT_GRAPH_ADJ_QUERY[r][c] = graph_i_adj_cstyle[r][c];
280
+
281
+ for (int j = i + 1; j < num_total_isos; ++j) {
282
+ bool temp_adj_for_gj[N_MAX_GED_CAP][N_MAX_GED_CAP];
283
+ mask_to_adj_matrix_small_N(available_canonical_masks[j], N_ACTUAL, temp_adj_for_gj);
284
+ for(int r=0; r<N_ACTUAL; ++r) for(int c=0; c<N_ACTUAL; ++c) graph_j_adj_stdarray[r][c] = temp_adj_for_gj[r][c];
285
+
286
+ all_pairwise_ged_cache[i][j] = all_pairwise_ged_cache[j][i] = min_edit_distance_global_perm_small_N(graph_j_adj_stdarray);
287
+ }
288
+ }
289
+ }
290
+
291
+ chosen_mask_indices_greedy.clear();
292
+ std::vector<bool> is_chosen_idx(num_total_isos, false);
293
+
294
+ if (num_total_isos > 0) {
295
+ if (mask_to_idx_map.count(0)) {
296
+ int zero_idx = mask_to_idx_map.at(0);
297
+ if (chosen_mask_indices_greedy.size() < (size_t)M_graphs) {
298
+ chosen_mask_indices_greedy.push_back(zero_idx);
299
+ is_chosen_idx[zero_idx] = true;
300
+ }
301
+ }
302
+ if (L_ACTUAL > 0 && chosen_mask_indices_greedy.size() < (size_t)M_graphs) {
303
+ uint16_t complete_mask_val = (1U << L_ACTUAL) - 1;
304
+ uint16_t canonical_complete_mask = get_canonical_mask(complete_mask_val);
305
+ if (mask_to_idx_map.count(canonical_complete_mask)) {
306
+ int complete_idx = mask_to_idx_map.at(canonical_complete_mask);
307
+ if (!is_chosen_idx[complete_idx]) {
308
+ chosen_mask_indices_greedy.push_back(complete_idx);
309
+ is_chosen_idx[complete_idx] = true;
310
+ }
311
+ }
312
+ }
313
+ }
314
+
315
+ for (int k_count = chosen_mask_indices_greedy.size(); k_count < M_graphs; ++k_count) {
316
+ if (chosen_mask_indices_greedy.size() >= (size_t)num_total_isos) {
317
+ break;
318
+ }
319
+
320
+ int best_new_idx_to_add = -1;
321
+ int max_of_min_distances_found = -1;
322
+
323
+ for (int cand_idx = 0; cand_idx < num_total_isos; ++cand_idx) {
324
+ if (is_chosen_idx[cand_idx]) continue;
325
+
326
+ int current_cand_min_dist_to_existing_G;
327
+ if (chosen_mask_indices_greedy.empty()) {
328
+ current_cand_min_dist_to_existing_G = L_ACTUAL + 1;
329
+ } else {
330
+ current_cand_min_dist_to_existing_G = L_ACTUAL + 1;
331
+ for (int chosen_idx : chosen_mask_indices_greedy) {
332
+ current_cand_min_dist_to_existing_G = std::min(current_cand_min_dist_to_existing_G, all_pairwise_ged_cache[cand_idx][chosen_idx]);
333
+ }
334
+ }
335
+
336
+ if (current_cand_min_dist_to_existing_G > max_of_min_distances_found) {
337
+ max_of_min_distances_found = current_cand_min_dist_to_existing_G;
338
+ best_new_idx_to_add = cand_idx;
339
+ }
340
+ }
341
+
342
+ if (best_new_idx_to_add != -1) {
343
+ chosen_mask_indices_greedy.push_back(best_new_idx_to_add);
344
+ is_chosen_idx[best_new_idx_to_add] = true;
345
+ } else {
346
+ break;
347
+ }
348
+ }
349
+
350
+ int num_distinct_chosen_graphs = chosen_mask_indices_greedy.size();
351
+ if (num_distinct_chosen_graphs < M_graphs) {
352
+ int fallback_idx = 0;
353
+ if (num_total_isos > 0) {
354
+ if (mask_to_idx_map.count(0)) {
355
+ fallback_idx = mask_to_idx_map.at(0);
356
+ }
357
+ }
358
+
359
+ for (int k_idx = num_distinct_chosen_graphs; k_idx < M_graphs; ++k_idx) {
360
+ if (num_total_isos > 0) {
361
+ chosen_mask_indices_greedy.push_back(fallback_idx);
362
+ } else {
363
+ chosen_mask_indices_greedy.push_back(0);
364
+ }
365
+ }
366
+ }
367
+ #ifdef DEBUG_OUTPUT
368
+ std::cerr << "# Chosen mask indices (size " << chosen_mask_indices_greedy.size() << "): ";
369
+ if (!available_canonical_masks.empty()){ // Check before accessing
370
+ for(int idx : chosen_mask_indices_greedy) {
371
+ if (idx < available_canonical_masks.size()) std::cerr << idx << " (" << available_canonical_masks[idx] << ") ";
372
+ else std::cerr << idx << " (OOB) ";
373
+ }
374
+ }
375
+ std::cerr << std::endl;
376
+ #endif
377
+
378
+ G_ADJS_CHOSEN_GED.resize(M_graphs);
379
+ for (int k_idx = 0; k_idx < M_graphs; ++k_idx) {
380
+ uint16_t mask_to_print = 0;
381
+ if (k_idx < chosen_mask_indices_greedy.size() &&
382
+ !available_canonical_masks.empty() &&
383
+ chosen_mask_indices_greedy[k_idx] < available_canonical_masks.size()) {
384
+ mask_to_print = available_canonical_masks[chosen_mask_indices_greedy[k_idx]];
385
+ } else if (L_ACTUAL == 0 && k_idx < chosen_mask_indices_greedy.size()) {
386
+ mask_to_print = 0;
387
+ }
388
+
389
+ bool temp_adj_cstyle[N_MAX_GED_CAP][N_MAX_GED_CAP];
390
+ mask_to_adj_matrix_small_N(mask_to_print, N_ACTUAL, temp_adj_cstyle);
391
+ for(int r=0; r<N_ACTUAL; ++r) for(int c=0; c<N_ACTUAL; ++c) G_ADJS_CHOSEN_GED[k_idx][r][c] = temp_adj_cstyle[r][c];
392
+
393
+ std::string s_out = "";
394
+ if (L_ACTUAL > 0) {
395
+ for (int bit_idx = 0; bit_idx < L_ACTUAL; ++bit_idx) {
396
+ s_out += ((mask_to_print >> bit_idx) & 1) ? '1' : '0';
397
+ }
398
+ }
399
+ std::cout << s_out << std::endl;
400
+ }
401
+
402
+ } else {
403
+ G_EDGE_COUNTS_LARGE_N.resize(M_graphs); G_STRINGS_CHOSEN_LARGE_N.resize(M_graphs);
404
+ if (M_graphs == 1) {
405
+ G_EDGE_COUNTS_LARGE_N[0] = (L_ACTUAL > 0) ? L_ACTUAL / 2 : 0;
406
+ } else {
407
+ for (int k=0; k<M_graphs; ++k) G_EDGE_COUNTS_LARGE_N[k] = static_cast<int>(std::round((double)k * L_ACTUAL / (M_graphs - 1.0)));
408
+
409
+ for (int k=0; k<M_graphs-1; ++k) {
410
+ if (G_EDGE_COUNTS_LARGE_N[k+1] <= G_EDGE_COUNTS_LARGE_N[k]) {
411
+ G_EDGE_COUNTS_LARGE_N[k+1] = G_EDGE_COUNTS_LARGE_N[k] + 1;
412
+ }
413
+ }
414
+ if (M_graphs > 0 && G_EDGE_COUNTS_LARGE_N[M_graphs-1] > L_ACTUAL) { // M_graphs > 0 check
415
+ int exceso = G_EDGE_COUNTS_LARGE_N[M_graphs-1] - L_ACTUAL;
416
+ for (int k=0; k<M_graphs; ++k) {
417
+ G_EDGE_COUNTS_LARGE_N[k] -= exceso;
418
+ }
419
+ }
420
+ for (int k=0; k<M_graphs; ++k) G_EDGE_COUNTS_LARGE_N[k] = std::min(L_ACTUAL, std::max(0, G_EDGE_COUNTS_LARGE_N[k]));
421
+
422
+ for (int k=0; k<M_graphs-1; ++k) {
423
+ G_EDGE_COUNTS_LARGE_N[k+1] = std::max(G_EDGE_COUNTS_LARGE_N[k+1], G_EDGE_COUNTS_LARGE_N[k] + 1);
424
+ }
425
+ for (int k=0; k<M_graphs; ++k) G_EDGE_COUNTS_LARGE_N[k] = std::min(L_ACTUAL, std::max(0, G_EDGE_COUNTS_LARGE_N[k]));
426
+ }
427
+
428
+ for (int k=0; k<M_graphs; ++k) {
429
+ G_STRINGS_CHOSEN_LARGE_N[k] = generate_random_graph_string_large_n(G_EDGE_COUNTS_LARGE_N[k], L_ACTUAL);
430
+ std::cout << G_STRINGS_CHOSEN_LARGE_N[k] << std::endl;
431
+ }
432
+ }
433
+ std::cout.flush(); // Explicit flush after all G_k are printed
434
+
435
+ for (int q_idx = 0; q_idx < 100; ++q_idx) {
436
+ std::string h_str; std::cin >> h_str;
437
+ if (current_strategy == Strategy::GED) {
438
+ if (M_graphs == 0) { std::cout << 0 << std::endl; std::cout.flush(); continue; }
439
+ if (G_ADJS_CHOSEN_GED.empty()){
440
+ #ifdef DEBUG_OUTPUT
441
+ std::cerr << "# Query " << q_idx << ": G_ADJS_CHOSEN_GED is empty but M_graphs=" << M_graphs << ". Outputting 0." << std::endl;
442
+ #endif
443
+ std::cout << 0 << std::endl; std::cout.flush(); continue;
444
+ }
445
+
446
+ string_to_adj_matrix_query(h_str, N_ACTUAL);
447
+
448
+ int best_g_idx = 0; int min_dist_found = L_ACTUAL + 2;
449
+ for (int j=0; j < M_graphs; ++j) {
450
+ if (j >= G_ADJS_CHOSEN_GED.size()) {
451
+ #ifdef DEBUG_OUTPUT
452
+ std::cerr << "# Query " << q_idx << ": Index j=" << j << " out of bounds for G_ADJS_CHOSEN_GED (size " << G_ADJS_CHOSEN_GED.size() << ")" << std::endl;
453
+ #endif
454
+ continue;
455
+ }
456
+ int dist = min_edit_distance_global_perm_small_N(G_ADJS_CHOSEN_GED[j]);
457
+ if (dist < min_dist_found) {
458
+ min_dist_found = dist;
459
+ best_g_idx = j;
460
+ }
461
+ }
462
+ std::cout << best_g_idx << std::endl;
463
+
464
+ } else {
465
+ if (M_graphs == 0) { std::cout << 0 << std::endl; std::cout.flush(); continue; }
466
+ if (G_EDGE_COUNTS_LARGE_N.empty()){
467
+ #ifdef DEBUG_OUTPUT
468
+ std::cerr << "# Query " << q_idx << ": G_EDGE_COUNTS_LARGE_N is empty but M_graphs=" << M_graphs << ". Outputting 0." << std::endl;
469
+ #endif
470
+ std::cout << 0 << std::endl; std::cout.flush(); continue;
471
+ }
472
+
473
+ int edges_Hk = count_set_bits_in_string(h_str);
474
+ int best_g_idx = 0; double min_abs_diff_expected_edges = -1.0;
475
+ for (int j=0; j<M_graphs; ++j) {
476
+ if (j >= G_EDGE_COUNTS_LARGE_N.size()) {
477
+ #ifdef DEBUG_OUTPUT
478
+ std::cerr << "# Query " << q_idx << ": Index j=" << j << " out of bounds for G_EDGE_COUNTS_LARGE_N (size " << G_EDGE_COUNTS_LARGE_N.size() << ")" << std::endl;
479
+ #endif
480
+ continue;
481
+ }
482
+ double expected_edges_Hk_from_Gj = (double)G_EDGE_COUNTS_LARGE_N[j] * (1.0 - 2.0*epsilon_noise_rate) + (double)L_ACTUAL * epsilon_noise_rate;
483
+ double diff = std::abs((double)edges_Hk - expected_edges_Hk_from_Gj);
484
+ if (min_abs_diff_expected_edges < -0.5 || diff < min_abs_diff_expected_edges) {
485
+ min_abs_diff_expected_edges = diff;
486
+ best_g_idx = j;
487
+ }
488
+ }
489
+ std::cout << best_g_idx << std::endl;
490
+ }
491
+ std::cout.flush(); // Explicit flush after each query prediction
492
+ }
493
+ return 0;
494
+ }
495
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc024/best_program.cpp ADDED
@@ -0,0 +1,626 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #pragma GCC optimize("O3,unroll-loops")
3
+
4
+ #include <iostream>
5
+ #include <vector>
6
+ #include <array>
7
+ #include <queue>
8
+ #include <algorithm> // For std::min, std::max, std::sort, std::unique, std::shuffle
9
+ #include <random> // For XorShift and std::shuffle
10
+ #include <chrono>
11
+ #include <utility> // For std::pair
12
+ #include <cmath> // For std::exp, std::pow
13
+ #include <climits> // For UINT_MAX
14
+
15
+ // --- Globals ---
16
+ const int N_FIXED = 50;
17
+ const int M_FIXED = 100; // Max ward ID, problem states M=100
18
+
19
+ std::vector<std::vector<int>> current_grid_state(N_FIXED, std::vector<int>(N_FIXED));
20
+ std::vector<std::vector<int>> best_grid_state(N_FIXED, std::vector<int>(N_FIXED));
21
+ int best_score_val = -1; // Stores count of 0-cells for the best state
22
+ int boundary_zero_cells_count = 0; // Number of zero cells located on the boundary
23
+ int last_boundary_zero_delta = 0; // Delta applied to boundary_zero_cells_count by the last successful attempt
24
+
25
+ struct XorShift {
26
+ unsigned int x, y, z, w;
27
+ XorShift() {
28
+ // Using std::random_device for better seed initialization
29
+ std::random_device rd;
30
+ x = rd();
31
+ y = rd();
32
+ z = rd();
33
+ w = rd();
34
+ // Ensure no zero initial state for w, which is common if rd() produces same values or all are 0
35
+ if (x == 0 && y == 0 && z == 0 && w == 0) w = 1; // Or any non-zero value
36
+ }
37
+ unsigned int next_uint() {
38
+ unsigned int t = x;
39
+ t ^= t << 11;
40
+ t ^= t >> 8;
41
+ x = y; y = z; z = w;
42
+ w ^= w >> 19;
43
+ w ^= t;
44
+ return w;
45
+ }
46
+ double next_double() { // In [0,1)
47
+ return (double)next_uint() / ((double)UINT_MAX + 1.0);
48
+ }
49
+ int next_int(int exclusive_max_val) { // In [0, exclusive_max_val - 1]
50
+ if (exclusive_max_val <= 0) return 0;
51
+ return next_uint() % exclusive_max_val;
52
+ }
53
+ // For std::shuffle
54
+ using result_type = unsigned int;
55
+ static constexpr unsigned int min() { return 0; }
56
+ static constexpr unsigned int max() { return UINT_MAX; }
57
+ unsigned int operator()() { return next_uint(); }
58
+ };
59
+ XorShift rnd_gen; // Global instance
60
+ auto G_START_TIME = std::chrono::high_resolution_clock::now();
61
+
62
+ double time_elapsed_ms() {
63
+ auto now = std::chrono::high_resolution_clock::now();
64
+ return std::chrono::duration<double, std::milli>(now - G_START_TIME).count();
65
+ }
66
+
67
+ struct AdjacencyInfo {
68
+ bool matrix[M_FIXED + 1][M_FIXED + 1];
69
+ AdjacencyInfo() {
70
+ for (int i = 0; i <= M_FIXED; ++i) for (int j = 0; j <= M_FIXED; ++j) matrix[i][j] = false;
71
+ }
72
+ void set_adj(int c1, int c2) {
73
+ if (c1 == c2) return;
74
+ matrix[std::min(c1, c2)][std::max(c1, c2)] = true;
75
+ }
76
+ bool is_adj(int c1, int c2) const {
77
+ if (c1 == c2) return false;
78
+ return matrix[std::min(c1, c2)][std::max(c1, c2)];
79
+ }
80
+ };
81
+ AdjacencyInfo required_adjacencies;
82
+ bool ward_has_any_req_adj[M_FIXED + 1];
83
+
84
+ struct BorderEdgeTracker {
85
+ int counts_arr[M_FIXED + 1][M_FIXED + 1];
86
+ BorderEdgeTracker() { clear(); }
87
+ void add_edge(int c1, int c2) {
88
+ if (c1 == c2) return;
89
+ counts_arr[std::min(c1, c2)][std::max(c1, c2)]++;
90
+ }
91
+ void remove_edge(int c1, int c2) {
92
+ if (c1 == c2) return;
93
+ counts_arr[std::min(c1, c2)][std::max(c1, c2)]--;
94
+ }
95
+ int get_count(int c1, int c2) const {
96
+ if (c1 == c2) return 0;
97
+ return counts_arr[std::min(c1, c2)][std::max(c1, c2)];
98
+ }
99
+ void clear() {
100
+ for (int i = 0; i <= M_FIXED; ++i) for (int j = 0; j <= M_FIXED; ++j) counts_arr[i][j] = 0;
101
+ }
102
+ };
103
+ BorderEdgeTracker current_border_edges_tracker;
104
+
105
+ std::vector<std::vector<std::pair<int, int>>> cells_by_color(M_FIXED + 1);
106
+ std::vector<std::vector<int>> pos_in_color_list(N_FIXED, std::vector<int>(N_FIXED));
107
+
108
+ unsigned int visited_marker_grid[N_FIXED][N_FIXED];
109
+ unsigned int current_visit_marker = 0;
110
+
111
+ std::queue<std::pair<int, int>> q_bfs_global;
112
+
113
+ const int DR[] = {-1, 1, 0, 0};
114
+ const int DC[] = {0, 0, -1, 1};
115
+
116
+ inline bool is_cell_on_grid(int r, int c) { return r >= 0 && r < N_FIXED && c >= 0 && c < N_FIXED; }
117
+
118
+ void increment_bfs_marker() {
119
+ current_visit_marker++;
120
+ if (current_visit_marker == 0) {
121
+ for (int i = 0; i < N_FIXED; ++i) {
122
+ for (int j = 0; j < N_FIXED; ++j) {
123
+ visited_marker_grid[i][j] = 0;
124
+ }
125
+ }
126
+ current_visit_marker = 1;
127
+ }
128
+ }
129
+
130
+ void clear_global_bfs_queue() {
131
+ std::queue<std::pair<int, int>> empty_queue;
132
+ std::swap(q_bfs_global, empty_queue);
133
+ }
134
+
135
+ void add_cell_to_color_ds(int r, int c, int color) {
136
+ cells_by_color[color].push_back({r,c});
137
+ pos_in_color_list[r][c] = cells_by_color[color].size() - 1;
138
+ }
139
+
140
+ void remove_cell_from_color_ds(int r, int c, int color) {
141
+ int idx_to_remove = pos_in_color_list[r][c];
142
+ std::pair<int,int> last_cell = cells_by_color[color].back();
143
+
144
+ cells_by_color[color][idx_to_remove] = last_cell;
145
+ pos_in_color_list[last_cell.first][last_cell.second] = idx_to_remove;
146
+
147
+ cells_by_color[color].pop_back();
148
+ }
149
+
150
+ void initialize_all_data_structures(const std::vector<std::vector<int>>& initial_grid) {
151
+ required_adjacencies = AdjacencyInfo();
152
+ current_border_edges_tracker.clear();
153
+ for(int i=0; i <= M_FIXED; ++i) cells_by_color[i].clear();
154
+
155
+ for (int i = 0; i < N_FIXED; ++i) {
156
+ for (int j = 0; j < N_FIXED; ++j) {
157
+ current_grid_state[i][j] = initial_grid[i][j];
158
+ add_cell_to_color_ds(i, j, initial_grid[i][j]);
159
+ }
160
+ }
161
+
162
+ for (int i = 0; i < N_FIXED; ++i) {
163
+ for (int j = 0; j < N_FIXED; ++j) {
164
+ int initial_color_val = initial_grid[i][j];
165
+ if (i == 0 || i == N_FIXED - 1 || j == 0 || j == N_FIXED - 1) {
166
+ required_adjacencies.set_adj(0, initial_color_val);
167
+ }
168
+ if (j + 1 < N_FIXED && initial_color_val != initial_grid[i][j+1]) {
169
+ required_adjacencies.set_adj(initial_color_val, initial_grid[i][j+1]);
170
+ }
171
+ if (i + 1 < N_FIXED && initial_color_val != initial_grid[i+1][j]) {
172
+ required_adjacencies.set_adj(initial_color_val, initial_grid[i+1][j]);
173
+ }
174
+
175
+ int current_color_val = current_grid_state[i][j];
176
+ if (i == 0) current_border_edges_tracker.add_edge(0, current_color_val);
177
+ if (i == N_FIXED - 1) current_border_edges_tracker.add_edge(0, current_color_val);
178
+ if (j == 0) current_border_edges_tracker.add_edge(0, current_color_val);
179
+ if (j == N_FIXED - 1) current_border_edges_tracker.add_edge(0, current_color_val);
180
+
181
+ if (j + 1 < N_FIXED && current_color_val != current_grid_state[i][j+1]) {
182
+ current_border_edges_tracker.add_edge(current_color_val, current_grid_state[i][j+1]);
183
+ }
184
+ if (i + 1 < N_FIXED && current_color_val != current_grid_state[i+1][j]) {
185
+ current_border_edges_tracker.add_edge(current_color_val, current_grid_state[i+1][j]);
186
+ }
187
+ }
188
+ }
189
+
190
+ for (int c1 = 0; c1 <= M_FIXED; ++c1) {
191
+ ward_has_any_req_adj[c1] = false;
192
+ for (int c2 = 0; c2 <= M_FIXED; ++c2) {
193
+ if (c1 == c2) continue;
194
+ if (required_adjacencies.is_adj(c1, c2)) {
195
+ ward_has_any_req_adj[c1] = true;
196
+ break;
197
+ }
198
+ }
199
+ }
200
+
201
+ boundary_zero_cells_count = 0;
202
+ for (int i = 0; i < N_FIXED; ++i) {
203
+ for (int j = 0; j < N_FIXED; ++j) {
204
+ if (current_grid_state[i][j] == 0 && (i == 0 || i == N_FIXED - 1 || j == 0 || j == N_FIXED - 1)) {
205
+ boundary_zero_cells_count++;
206
+ }
207
+ }
208
+ }
209
+ best_grid_state = current_grid_state;
210
+ best_score_val = cells_by_color[0].size();
211
+ }
212
+
213
+ bool check_region_connectivity_bfs(int target_color) {
214
+ const auto& cells_of_target_color = cells_by_color[target_color];
215
+ if (cells_of_target_color.empty()) return true;
216
+
217
+ increment_bfs_marker();
218
+ clear_global_bfs_queue();
219
+
220
+ q_bfs_global.push(cells_of_target_color[0]);
221
+ visited_marker_grid[cells_of_target_color[0].first][cells_of_target_color[0].second] = current_visit_marker;
222
+
223
+ int count_visited_cells = 0;
224
+ while (!q_bfs_global.empty()) {
225
+ std::pair<int, int> curr = q_bfs_global.front();
226
+ q_bfs_global.pop();
227
+ count_visited_cells++;
228
+
229
+ for (int k = 0; k < 4; ++k) {
230
+ int nr = curr.first + DR[k];
231
+ int nc = curr.second + DC[k];
232
+ if (is_cell_on_grid(nr, nc) &&
233
+ current_grid_state[nr][nc] == target_color &&
234
+ visited_marker_grid[nr][nc] != current_visit_marker) {
235
+ visited_marker_grid[nr][nc] = current_visit_marker;
236
+ q_bfs_global.push({nr, nc});
237
+ }
238
+ }
239
+ }
240
+ return count_visited_cells == cells_of_target_color.size();
241
+ }
242
+
243
+ bool check_region_0_connectivity_full() {
244
+ const auto& cells_c0 = cells_by_color[0];
245
+ if (cells_c0.empty()) {
246
+ return true;
247
+ }
248
+
249
+ increment_bfs_marker();
250
+ clear_global_bfs_queue();
251
+
252
+ bool any_boundary_zero_cell_found = false;
253
+ for (const auto& cell_coord : cells_c0) {
254
+ int r = cell_coord.first;
255
+ int c = cell_coord.second;
256
+ if (r == 0 || r == N_FIXED - 1 || c == 0 || c == N_FIXED - 1) {
257
+ if (visited_marker_grid[r][c] != current_visit_marker) {
258
+ q_bfs_global.push(cell_coord);
259
+ visited_marker_grid[r][c] = current_visit_marker;
260
+ }
261
+ any_boundary_zero_cell_found = true;
262
+ }
263
+ }
264
+
265
+ if (!any_boundary_zero_cell_found) {
266
+ return false;
267
+ }
268
+
269
+ while (!q_bfs_global.empty()) {
270
+ std::pair<int, int> curr = q_bfs_global.front();
271
+ q_bfs_global.pop();
272
+
273
+ for (int k_dir = 0; k_dir < 4; ++k_dir) {
274
+ int nr = curr.first + DR[k_dir];
275
+ int nc = curr.second + DC[k_dir];
276
+ if (is_cell_on_grid(nr, nc) &&
277
+ current_grid_state[nr][nc] == 0 &&
278
+ visited_marker_grid[nr][nc] != current_visit_marker) {
279
+ visited_marker_grid[nr][nc] = current_visit_marker;
280
+ q_bfs_global.push({nr, nc});
281
+ }
282
+ }
283
+ }
284
+
285
+ for (const auto& cell_coord : cells_c0) {
286
+ if (visited_marker_grid[cell_coord.first][cell_coord.second] != current_visit_marker) {
287
+ return false;
288
+ }
289
+ }
290
+ return true;
291
+ }
292
+
293
+ /*
294
+ SmallAdjDelta: A tiny fixed-size accumulator for adjacency count deltas
295
+ between color pairs caused by a single-cell recoloring. We only ever touch
296
+ up to 8 distinct pairs (4 neighbors x 2 old/new), so linear lookup is fine.
297
+ */
298
+ struct SmallAdjDelta {
299
+ int c1[16], c2[16], delta[16], sz;
300
+ inline void clear() { sz = 0; }
301
+ inline void add(int a, int b, int d) {
302
+ if (a == b) return;
303
+ if (a > b) std::swap(a, b);
304
+ for (int i = 0; i < sz; ++i) {
305
+ if (c1[i] == a && c2[i] == b) { delta[i] += d; return; }
306
+ }
307
+ c1[sz] = a; c2[sz] = b; delta[sz] = d; sz++;
308
+ }
309
+ } temp_adj_deltas;
310
+
311
+ /*
312
+ removal_keeps_connectivity_local:
313
+ Checks if removing cell (r,c) of 'color' keeps that color's region connected.
314
+ Assumes current_grid_state[r][c] is already set to a different color (i.e., removal applied).
315
+ Fast path: if the number of same-color neighbors of (r,c) is <= 1, it can't split the region.
316
+ Otherwise, BFS from one neighbor and ensure all other neighbors are reachable without passing through (r,c).
317
+ */
318
+ inline bool removal_keeps_connectivity_local(int r, int c, int color) {
319
+ int nr[4], nc[4], cnt = 0;
320
+ for (int k = 0; k < 4; ++k) {
321
+ int rr = r + DR[k], cc = c + DC[k];
322
+ if (is_cell_on_grid(rr, cc) && current_grid_state[rr][cc] == color) {
323
+ nr[cnt] = rr; nc[cnt] = cc; cnt++;
324
+ }
325
+ }
326
+ if (cnt <= 1) return true;
327
+
328
+ increment_bfs_marker();
329
+ clear_global_bfs_queue();
330
+
331
+ bool found_nei[4] = {false, false, false, false};
332
+ q_bfs_global.push({nr[0], nc[0]});
333
+ visited_marker_grid[nr[0]][nc[0]] = current_visit_marker;
334
+ found_nei[0] = true;
335
+ int found = 1;
336
+
337
+ while (!q_bfs_global.empty() && found < cnt) {
338
+ auto cur = q_bfs_global.front(); q_bfs_global.pop();
339
+ for (int k = 0; k < 4; ++k) {
340
+ int rr = cur.first + DR[k], cc = cur.second + DC[k];
341
+ if (!is_cell_on_grid(rr, cc)) continue;
342
+ if (rr == r && cc == c) continue;
343
+ if (current_grid_state[rr][cc] != color) continue;
344
+ if (visited_marker_grid[rr][cc] == current_visit_marker) continue;
345
+ visited_marker_grid[rr][cc] = current_visit_marker;
346
+ q_bfs_global.push({rr, cc});
347
+ for (int i = 0; i < cnt; ++i) {
348
+ if (!found_nei[i] && nr[i] == rr && nc[i] == cc) {
349
+ found_nei[i] = true;
350
+ found++;
351
+ break;
352
+ }
353
+ }
354
+ }
355
+ }
356
+ return found == cnt;
357
+ }
358
+
359
+ /*
360
+ has_adjacent_color:
361
+ Returns true if any of the 4-neighbors of (r,c) has the specified 'color'.
362
+ */
363
+ inline bool has_adjacent_color(int r, int c, int color) {
364
+ for (int k = 0; k < 4; ++k) {
365
+ int rr = r + DR[k], cc = c + DC[k];
366
+ if (is_cell_on_grid(rr, cc) && current_grid_state[rr][cc] == color) return true;
367
+ }
368
+ return false;
369
+ }
370
+
371
+ bool attempt_change_cell_color_and_validate(int r, int c, int old_color, int new_color) {
372
+ /*
373
+ Docstring: Lightweight recolor validator and applier.
374
+ - Tentatively recolor and enforce only cheap local connectivity constraints (per-color, and 0 vs boundary).
375
+ - Update adjacency counts for only touched pairs and verify consistency with the required adjacency.
376
+ - Track boundary-0 delta so the SA step can undo a rejected-but-valid move cheaply.
377
+ Note: We avoid extra pre-mutation pruning to reduce overhead; the post-update validation
378
+ on the affected pairs is sufficient to ensure legality.
379
+ */
380
+ // Precompute counts before mutation
381
+ int old_count_old = (int)cells_by_color[old_color].size();
382
+ int old_count_new = (int)cells_by_color[new_color].size();
383
+ int old_zero_count = (int)cells_by_color[0].size();
384
+
385
+ // Early local feasibility pruning (no mutations yet)
386
+ bool on_boundary = (r == 0 || r == N_FIXED - 1 || c == 0 || c == N_FIXED - 1);
387
+ if (new_color != 0) {
388
+ if (old_count_new > 0 && !has_adjacent_color(r, c, new_color)) return false;
389
+ } else {
390
+ if (old_zero_count == 0) {
391
+ if (!on_boundary) return false;
392
+ } else {
393
+ if (!has_adjacent_color(r, c, 0) && !on_boundary) return false;
394
+ }
395
+ }
396
+
397
+ // Apply recolor to the working state
398
+ current_grid_state[r][c] = new_color;
399
+ remove_cell_from_color_ds(r, c, old_color);
400
+ add_cell_to_color_ds(r, c, new_color);
401
+
402
+ // Boundary 0 delta for this move (do not commit yet)
403
+ int boundary_delta = 0;
404
+ // 'on_boundary' computed earlier
405
+ if (on_boundary) {
406
+ if (old_color == 0) boundary_delta--;
407
+ if (new_color == 0) boundary_delta++;
408
+ }
409
+
410
+ // Quick local connectivity constraints
411
+ bool ok = true;
412
+
413
+ // New color connectivity
414
+ if (new_color != 0) {
415
+ if (old_count_new > 0) { // color already existed before adding this cell
416
+ if (!has_adjacent_color(r, c, new_color)) ok = false;
417
+ }
418
+ } else {
419
+ // new color is 0
420
+ if (old_zero_count == 0) {
421
+ if (!on_boundary) ok = false; // first zero must touch boundary
422
+ } else {
423
+ // must attach to existing zero or boundary to keep 0 (with outside) connected
424
+ if (!has_adjacent_color(r, c, 0) && !on_boundary) ok = false;
425
+ }
426
+ }
427
+
428
+ // Old color connectivity (after removal)
429
+ if (ok && old_color != 0 && old_count_old > 1) {
430
+ if (!removal_keeps_connectivity_local(r, c, old_color)) ok = false;
431
+ }
432
+ if (ok && old_color == 0 && old_zero_count > 1) {
433
+ if (!removal_keeps_connectivity_local(r, c, 0)) ok = false;
434
+ if (ok) {
435
+ int new_zero_count = old_zero_count - 1;
436
+ if (new_zero_count > 0) {
437
+ if (boundary_zero_cells_count + boundary_delta <= 0) ok = false;
438
+ }
439
+ }
440
+ }
441
+
442
+ if (!ok) {
443
+ // Revert recolor
444
+ current_grid_state[r][c] = old_color;
445
+ remove_cell_from_color_ds(r, c, new_color);
446
+ add_cell_to_color_ds(r, c, old_color);
447
+ return false;
448
+ }
449
+
450
+ // Prepare adjacency deltas and apply to the tracker
451
+ temp_adj_deltas.clear();
452
+ for (int k_adj = 0; k_adj < 4; ++k_adj) {
453
+ int nr = r + DR[k_adj];
454
+ int nc = c + DC[k_adj];
455
+ int neighbor_actual_color = is_cell_on_grid(nr, nc) ? current_grid_state[nr][nc] : 0;
456
+
457
+ if (old_color != neighbor_actual_color) temp_adj_deltas.add(old_color, neighbor_actual_color, -1);
458
+ if (new_color != neighbor_actual_color) temp_adj_deltas.add(new_color, neighbor_actual_color, +1);
459
+ }
460
+ for (int i = 0; i < temp_adj_deltas.sz; ++i) {
461
+ int c1 = temp_adj_deltas.c1[i], c2 = temp_adj_deltas.c2[i], d = temp_adj_deltas.delta[i];
462
+ if (d > 0) for (int t = 0; t < d; ++t) current_border_edges_tracker.add_edge(c1, c2);
463
+ else for (int t = 0; t < -d; ++t) current_border_edges_tracker.remove_edge(c1, c2);
464
+ }
465
+
466
+ // Verify adjacency constraints for only affected pairs
467
+ for (int i = 0; i < temp_adj_deltas.sz; ++i) {
468
+ int c1 = temp_adj_deltas.c1[i], c2 = temp_adj_deltas.c2[i];
469
+ bool has_edge_now = current_border_edges_tracker.get_count(c1, c2) > 0;
470
+ bool needs_edge = required_adjacencies.is_adj(c1, c2);
471
+ if (has_edge_now != needs_edge) { ok = false; break; }
472
+ }
473
+
474
+ // Cannot delete a ward that must exist (non-zero)
475
+ if (ok && old_color != 0 && cells_by_color[old_color].empty() && ward_has_any_req_adj[old_color]) ok = false;
476
+
477
+ if (!ok) {
478
+ // Revert adjacency tracker
479
+ for (int i = 0; i < temp_adj_deltas.sz; ++i) {
480
+ int c1 = temp_adj_deltas.c1[i], c2 = temp_adj_deltas.c2[i], d = temp_adj_deltas.delta[i];
481
+ if (d > 0) for (int t = 0; t < d; ++t) current_border_edges_tracker.remove_edge(c1, c2);
482
+ else for (int t = 0; t < -d; ++t) current_border_edges_tracker.add_edge(c1, c2);
483
+ }
484
+ // Revert recolor
485
+ current_grid_state[r][c] = old_color;
486
+ remove_cell_from_color_ds(r, c, new_color);
487
+ add_cell_to_color_ds(r, c, old_color);
488
+ return false;
489
+ }
490
+
491
+ // Commit boundary zero cells count (caller will undo on SA rejection)
492
+ boundary_zero_cells_count += boundary_delta;
493
+ last_boundary_zero_delta = boundary_delta;
494
+ return true;
495
+ }
496
+
497
+ void solve_main_logic() {
498
+ std::vector<std::vector<int>> initial_grid_from_input(N_FIXED, std::vector<int>(N_FIXED));
499
+ for (int i = 0; i < N_FIXED; ++i) for (int j = 0; j < N_FIXED; ++j) std::cin >> initial_grid_from_input[i][j];
500
+
501
+ initialize_all_data_structures(initial_grid_from_input);
502
+
503
+ const double GREEDY_PASS_BUDGET_MS = 300.0;
504
+ double greedy_pass_start_abs_time = time_elapsed_ms();
505
+
506
+ std::vector<std::pair<int,int>> all_cells_shuffled;
507
+ all_cells_shuffled.reserve(N_FIXED * N_FIXED);
508
+ for(int r_idx=0; r_idx<N_FIXED; ++r_idx) for(int c_idx=0; c_idx<N_FIXED; ++c_idx) all_cells_shuffled.push_back({r_idx,c_idx});
509
+
510
+ std::shuffle(all_cells_shuffled.begin(), all_cells_shuffled.end(), rnd_gen);
511
+ for (const auto& cell_coords : all_cells_shuffled) {
512
+ if (time_elapsed_ms() - greedy_pass_start_abs_time > GREEDY_PASS_BUDGET_MS) break;
513
+
514
+ int r = cell_coords.first; int c = cell_coords.second;
515
+ int original_color = current_grid_state[r][c];
516
+ if (original_color == 0) continue;
517
+
518
+ if (attempt_change_cell_color_and_validate(r, c, original_color, 0)) {
519
+ int current_zeros_count = cells_by_color[0].size();
520
+ if (current_zeros_count > best_score_val) {
521
+ best_score_val = current_zeros_count;
522
+ best_grid_state = current_grid_state;
523
+ }
524
+ }
525
+ }
526
+
527
+ double sa_start_temp = 2.0;
528
+ double sa_end_temp = 0.01;
529
+ const double TOTAL_COMPUTATION_TIME_MS = 1950.0;
530
+
531
+ double sa_start_abs_time = time_elapsed_ms();
532
+ double sa_total_duration_ms = TOTAL_COMPUTATION_TIME_MS - sa_start_abs_time;
533
+ if (sa_total_duration_ms <= 0) sa_total_duration_ms = 1.0;
534
+
535
+ int iter_count = 0;
536
+ while(true) {
537
+ iter_count++;
538
+ if(iter_count % 256 == 0) {
539
+ if (time_elapsed_ms() >= TOTAL_COMPUTATION_TIME_MS) break;
540
+ }
541
+
542
+ double time_spent_in_sa = time_elapsed_ms() - sa_start_abs_time;
543
+ double progress_ratio = (sa_total_duration_ms > 1e-9) ? (time_spent_in_sa / sa_total_duration_ms) : 1.0;
544
+ progress_ratio = std::min(progress_ratio, 1.0);
545
+
546
+ double current_temperature = sa_start_temp * std::pow(sa_end_temp / sa_start_temp, progress_ratio);
547
+ current_temperature = std::max(current_temperature, sa_end_temp);
548
+
549
+ int r_coord = rnd_gen.next_int(N_FIXED);
550
+ int c_coord = rnd_gen.next_int(N_FIXED);
551
+ int original_color_at_cell = current_grid_state[r_coord][c_coord];
552
+
553
+ // Build a tiny unique candidate set: {0} U neighbors (with out-of-grid treated as 0),
554
+ // sampled uniformly (duplicates bias toward 0 near the boundary).
555
+ int candidate_new_colors[5];
556
+ int num_candidate_options = 0;
557
+ candidate_new_colors[num_candidate_options++] = 0;
558
+ for(int k_neighbor_idx=0; k_neighbor_idx<4; ++k_neighbor_idx) {
559
+ int nr = r_coord + DR[k_neighbor_idx];
560
+ int nc = c_coord + DC[k_neighbor_idx];
561
+ if (is_cell_on_grid(nr,nc)) {
562
+ candidate_new_colors[num_candidate_options++] = current_grid_state[nr][nc];
563
+ } else {
564
+ candidate_new_colors[num_candidate_options++] = 0;
565
+ }
566
+ }
567
+ int new_proposed_color = candidate_new_colors[rnd_gen.next_int(num_candidate_options)];
568
+
569
+ if (original_color_at_cell == new_proposed_color) continue;
570
+
571
+ int delta_in_score_metric = 0;
572
+ if (new_proposed_color == 0 && original_color_at_cell != 0) delta_in_score_metric = 1;
573
+ else if (new_proposed_color != 0 && original_color_at_cell == 0) delta_in_score_metric = -1;
574
+
575
+ if (attempt_change_cell_color_and_validate(r_coord, c_coord, original_color_at_cell, new_proposed_color)) {
576
+ bool accept_this_move = false;
577
+ if (delta_in_score_metric >= 0) {
578
+ accept_this_move = true;
579
+ if (cells_by_color[0].size() > best_score_val) {
580
+ best_score_val = cells_by_color[0].size();
581
+ best_grid_state = current_grid_state;
582
+ }
583
+ } else {
584
+ if (current_temperature > 1e-9 && rnd_gen.next_double() < std::exp((double)delta_in_score_metric / current_temperature)) {
585
+ accept_this_move = true;
586
+ } else {
587
+ accept_this_move = false;
588
+ }
589
+ }
590
+
591
+ if (!accept_this_move) {
592
+ current_grid_state[r_coord][c_coord] = original_color_at_cell;
593
+ remove_cell_from_color_ds(r_coord, c_coord, new_proposed_color);
594
+ add_cell_to_color_ds(r_coord, c_coord, original_color_at_cell);
595
+ // Revert boundary zero count
596
+ boundary_zero_cells_count -= last_boundary_zero_delta;
597
+ last_boundary_zero_delta = 0;
598
+
599
+ for (int i = 0; i < temp_adj_deltas.sz; ++i) {
600
+ int c1_ = temp_adj_deltas.c1[i], c2_ = temp_adj_deltas.c2[i], delta = temp_adj_deltas.delta[i];
601
+ if (delta > 0) for (int t = 0; t < delta; ++t) current_border_edges_tracker.remove_edge(c1_, c2_);
602
+ else for (int t = 0; t < -delta; ++t) current_border_edges_tracker.add_edge(c1_, c2_);
603
+ }
604
+ }
605
+ }
606
+ }
607
+
608
+ for (int i = 0; i < N_FIXED; ++i) {
609
+ for (int j = 0; j < N_FIXED; ++j) {
610
+ std::cout << best_grid_state[i][j] << (j == N_FIXED - 1 ? "" : " ");
611
+ }
612
+ std::cout << std::endl;
613
+ }
614
+ }
615
+
616
+ int main() {
617
+ std::ios_base::sync_with_stdio(false); std::cin.tie(NULL);
618
+ G_START_TIME = std::chrono::high_resolution_clock::now();
619
+
620
+ int n_in_dummy, m_in_dummy;
621
+ std::cin >> n_in_dummy >> m_in_dummy;
622
+
623
+ solve_main_logic();
624
+ return 0;
625
+ }
626
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc024/config.yaml ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ALE-Bench ahc024 — AtCoder Heuristic Contest
2
+ # Usage: skydiscover-run initial_program.cpp evaluator.py -c config.yaml -s <strategy>
3
+ language: cpp
4
+ diff_based_generation: true
5
+ max_iterations: 100
6
+ checkpoint_interval: 10
7
+ max_solution_length: 60000
8
+ llm:
9
+ api_base: https://api.openai.com/v1
10
+ models:
11
+ - name: "gpt-5"
12
+ weight: 1.0
13
+ max_tokens: 32000
14
+ timeout: 600
15
+ prompt:
16
+ system_message: "You are a world-class algorithm engineer, and you are very good at programming. Now, you are participating\
17
+ \ in a programming contest. You are asked to solve a heuristic problem, known as an NP-hard problem.\n\nStory\n--------\n\
18
+ Mr. Takahashi, the mayor of Takahashi City, decided to draw a map of Takahashi City on the floor of the city hall lobby\
19
+ \ using colored square tiles.\nTakahashi City is divided into several wards, and in this map, each ward should be represented\
20
+ \ as a set of connected tiles of the same color.\nHe commissioned a contractor to create a draft of an accurate map, but\
21
+ \ the number of tiles to be used was too large, and the budget was exceeded.\nMayor Takahashi, who loves graphs, is only\
22
+ \ interested in the adjacencies between the wards and thinks that the map could be drawn with fewer tiles if information\
23
+ \ other than adjacencies, such as the shape and size of each ward, is ignored.\nPlease create a map using as few tiles\
24
+ \ as possible.\n\n<div style=\"display: flex; width: 100%;\">\n <div style=\"flex-basis: 40%; text-align: center; margin-right:\
25
+ \ 10%;\">\n <img src=\"./images/input.png\" style=\"max-width: 100%; max-height: 100%; vertical-align: middle;\">\n\
26
+ \ <p>Accurate map</p>\n </div>\n <div style=\"flex-basis: 50%; text-align: center;\">\n <img src=\"./images/output.png\"\
27
+ \ style=\"max-width: 100%; max-height: 100%; vertical-align: middle;\">\n <p>Small map correctly representing adjacencies</p>\n\
28
+ \ </div>\n</div>\n\nProblem Statement\n--------\nGiven a map of Takahashi City represented on a grid of $n\\times n$\
29
+ \ squares.\nLet $(0,0)$ be the coordinates of the top-left square, and $(i,j)$ be the coordinates of the square located\
30
+ \ $i$ squares down and $j$ squares to the right from there.\nThe city consists of $m$ wards, and the square of color $c$\
31
+ \ ($1\\leq c\\leq m$) corresponds to the $c$-th ward.\nThe outside of the $n\\times n$ squares correspond to the outside\
32
+ \ of the city and is colored $0$.\n\nTwo squares are defined as \"adjacent\" if they share an edge, and a set of squares\
33
+ \ is defined as \"connected\" if any two squares can reach each other via adjacent squares.\nIn the given map, for each\
34
+ \ color c, the set of squares of color c is guaranteed to be connected.\n\nYour task is to create a map represented on\
35
+ \ a grid of $n\\times n$ squares that satisfies all of the following conditions.\n\n- For every color $c$ ($0\\leq c\\\
36
+ leq m$), squares of color $c$ must be connected. Note that since the outside of the $n\\times n$ squares is colored $0$,\
37
+ \ squares of color $0$ can be connected through the outside squares.\n- For every pair of colors $c$ and $d$ ($0\\leq\
38
+ \ c<d\\leq m$), the adjacency of a set of squares of color $c$ and a set of squares of color $d$ in the original map and\
39
+ \ the created map must be identical. That is, if and only if there exist adjacent squares of color $c$ and $d$ in the\
40
+ \ original map, there exist adjacent squares of color $c$ and $d$ in the created map. Note that since the outside of the\
41
+ \ $n\\times n$ squares is colored $0$, the squares on the boundary are considered to be adjacent to squares of color $0$.\n\
42
+ \n\nScoring\n--------\nLet $E$ be the total number of squares of color $0$ in the created map.\nThen you will obtain a\
43
+ \ score of $E+1$.\n\nThere are 150 test cases, and the score of a submission is the total score for each test case.\n\
44
+ If your submission produces an illegal output or exceeds the time limit for some test cases, the submission itself will\
45
+ \ be judged as <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Wrong Answer\">WA</span>\
46
+ \ or <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Time Limit Exceeded\">TLE</span>\
47
+ \ , and the score of the submission will be zero.\nThe highest score obtained during the contest will determine the final\
48
+ \ ranking, and there will be no system test after the contest.\nIf more than one participant gets the same score, they\
49
+ \ will be ranked in the same place regardless of the submission time.\n\n\n\nInput\n--------\nInput is given from Standard\
50
+ \ Input in the following format.\n\n~~~\n$n$ $m$\n$c_{0,0}$ $c_{0,1}$ $\\cdots$ $c_{0,n-1}$\n$\\vdots$\n$c_{n-1,0}$ $c_{n-1,1}$\
51
+ \ $\\cdots$ $c_{n-1,n-1}$\n~~~\n\nFor all test cases, we fix $n = 50$ and $m = 100$.\n$c_{i,j}$ is an integer value representing\
52
+ \ the color of the square at coordinates $(i,j)$ and satisfies $1\\leq c_{i,j}\\leq m$.\nFor every $k=1,2,\\cdots,m$,\
53
+ \ there exists at least one $(i,j)$ with $c_{i,j}=k$.\n\n\nOutput\n--------\nLet $d_{i,j}$ ($0\\leq d_{i,j}\\leq m$) be\
54
+ \ the color of the square at coordinates $(i,j)$ in the created map.\nThen, output to Standard Output in the following\
55
+ \ format.\n\n~~~\n$d_{0,0}$ $d_{0,1}$ $\\cdots$ $d_{0,n-1}$\n$\\vdots$\n$d_{n-1,0}$ $d_{n-1,1}$ $\\cdots$ $d_{n-1,n-1}$\n\
56
+ ~~~\n\nIf the output map does not satisfy the conditions specified in the problem statement, the submission will be judged\
57
+ \ as <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Wrong Answer\">WA</span>.\n\n\
58
+ Your program may output multiple solutions.\nIf multiple solutions are output, only the last one is used for scoring.\n\
59
+ You can compare multiple solutions using the web version of the visualizer.\n\n<a href=\"https://img.atcoder.jp/ahc024/AU5KcDyn.html?lang=en&seed=0&output=sample\"\
60
+ >Show example</a>\n\n\nInput Generation\n--------\n<details>\nFirst, we initialize with $c_{i,j}=0$ for all $(i,j)$.\n\
61
+ Next, for each $k=1,2,\\cdots,m$, we randomly select a square with $c_{i,j}=0$ and set $c_{i,j}=k$.\nFinally, we repeat\
62
+ \ the following process while squares with $c_{i,j}=0$ remain.\n\nRandomly select a square with $c_{i,j}=0$ and randomly\
63
+ \ select its adjacent square $(i',j')$.\nWe set $c_{i,j}=c_{i',j'}$.\n</details>\n\nTools (Input generator and visualizer)\n\
64
+ --------\n- <a href=\"https://img.atcoder.jp/ahc024/AU5KcDyn.html?lang=en\">Web version</a>: This is more powerful than\
65
+ \ the local version providing animations and manual play.\n- <a href=\"https://img.atcoder.jp/ahc024/AU5KcDyn.zip\">Local\
66
+ \ version</a>: You need a compilation environment of <a href=\"https://www.rust-lang.org/\">Rust language</a>.\n - <a\
67
+ \ href=\"https://img.atcoder.jp/ahc024/AU5KcDyn_windows.zip\">Pre-compiled binary for Windows</a>: If you are not familiar\
68
+ \ with the Rust language environment, please use this instead.\n\nPlease be aware that sharing visualization results or\
69
+ \ discussing solutions/ideas during the contest is prohibited.\n\n\n Problem constraints:\n time_limit=2.0 memory_limit=1073741824\n"
70
+ evaluator:
71
+ timeout: 10000
72
+ cascade_evaluation: false
73
+
benchmarks/ale_bench/ale-bench-lite-problems/ahc024/evaluator.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+ from pathlib import Path
3
+ from ale_bench.result import CaseResult, JudgeResult, Result
4
+ from ale_bench_eval.safe_ale_session import start_ale_bench_session
5
+ import logging
6
+ import sys
7
+ logger = logging.getLogger(__name__ + "_" + "ALE_BENCH_EVALUATOR")
8
+
9
+ def result_feedback(result: Result) -> CaseResult:
10
+ if result.overall_judge_result == JudgeResult.ACCEPTED:
11
+ return result.case_results[0]
12
+ else:
13
+ selected_case_idx = 0
14
+ for idx, case_result in enumerate(result.case_results):
15
+ if case_result.judge_result == result.overall_judge_result:
16
+ selected_case_idx = idx
17
+ break
18
+ return result.case_results[selected_case_idx]
19
+
20
+ def evaluate(program_path):
21
+ problem_id = "ahc024"
22
+ logger.info(f"Evaluating program {program_path} for problem {problem_id} in ale bench evaluator")
23
+ try:
24
+ session = None
25
+ logger.info("Starting ALE-Bench session")
26
+ session = start_ale_bench_session(
27
+ problem_id=problem_id,
28
+ lite_version=True,
29
+ num_workers=13,
30
+ )
31
+ logger.info("ALE-Bench session started")
32
+ if not session:
33
+ raise RuntimeError("Failed to start or restart the session.")
34
+ optim_factor = 1 if session.problem.metadata.score_type == "maximize" else -1
35
+ code = Path(program_path).read_text().replace("# EVOLVE-BLOCK-START", "").replace("# EVOLVE-BLOCK-END", "").strip()
36
+ logger.info("Code extracted")
37
+ num_public_cases = 50
38
+ cases = session.case_gen(list(range(num_public_cases)))
39
+ public_result = session.case_eval(
40
+ cases, code, code_language="cpp20", skip_local_visualization=True
41
+ )
42
+ logger.info("Public evaluation completed")
43
+ extracted_case = result_feedback(public_result)
44
+ logger.info("Result feedback completed")
45
+ logger.info("ALE-Bench session closed")
46
+ combined_score = public_result.overall_absolute_score * optim_factor / num_public_cases
47
+ if public_result.overall_judge_result != JudgeResult.ACCEPTED and optim_factor == -1:
48
+ combined_score = -sys.maxsize - 1
49
+ session.close()
50
+ return {
51
+ "judge_result": public_result.overall_judge_result.value,
52
+ "overall_score": public_result.overall_absolute_score,
53
+ "max_execution_time_sec": max([case_result.execution_time for case_result in public_result.case_results]),
54
+ "max_memory_usage_mib": max([case_result.memory_usage for case_result in public_result.case_results]) // 1024 // 1024,
55
+ "standard_error": extracted_case.error_str,
56
+ "message": extracted_case.message,
57
+ "combined_score": combined_score,
58
+ }
59
+ except Exception as e:
60
+ logger.error(f"Evaluation failed completely: {str(e)}")
61
+ logger.error(traceback.format_exc())
62
+ return {
63
+ "overall_score": 0.0,
64
+ "error": str(e),
65
+ }
benchmarks/ale_bench/ale-bench-lite-problems/ahc024/initial_program.cpp ADDED
@@ -0,0 +1,481 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #pragma GCC optimize("O3,unroll-loops")
3
+
4
+ #include <iostream>
5
+ #include <vector>
6
+ #include <map> // For temp_adj_deltas_map_global
7
+ #include <queue>
8
+ #include <algorithm> // For std::min, std::max, std::sort, std::unique, std::shuffle
9
+ #include <random> // For XorShift and std::shuffle
10
+ #include <chrono>
11
+ #include <utility> // For std::pair
12
+ #include <cmath> // For std::exp, std::pow
13
+ #include <climits> // For UINT_MAX
14
+
15
+ // --- Globals ---
16
+ const int N_FIXED = 50;
17
+ const int M_FIXED = 100; // Max ward ID, problem states M=100
18
+
19
+ std::vector<std::vector<int>> current_grid_state(N_FIXED, std::vector<int>(N_FIXED));
20
+ std::vector<std::vector<int>> best_grid_state(N_FIXED, std::vector<int>(N_FIXED));
21
+ int best_score_val = -1; // Stores count of 0-cells for the best state
22
+
23
+ struct XorShift {
24
+ unsigned int x, y, z, w;
25
+ XorShift() {
26
+ // Using std::random_device for better seed initialization
27
+ std::random_device rd;
28
+ x = rd();
29
+ y = rd();
30
+ z = rd();
31
+ w = rd();
32
+ // Ensure no zero initial state for w, which is common if rd() produces same values or all are 0
33
+ if (x == 0 && y == 0 && z == 0 && w == 0) w = 1; // Or any non-zero value
34
+ }
35
+ unsigned int next_uint() {
36
+ unsigned int t = x;
37
+ t ^= t << 11;
38
+ t ^= t >> 8;
39
+ x = y; y = z; z = w;
40
+ w ^= w >> 19;
41
+ w ^= t;
42
+ return w;
43
+ }
44
+ double next_double() { // In [0,1)
45
+ return (double)next_uint() / ((double)UINT_MAX + 1.0);
46
+ }
47
+ int next_int(int exclusive_max_val) { // In [0, exclusive_max_val - 1]
48
+ if (exclusive_max_val <= 0) return 0;
49
+ return next_uint() % exclusive_max_val;
50
+ }
51
+ // For std::shuffle
52
+ using result_type = unsigned int;
53
+ static constexpr unsigned int min() { return 0; }
54
+ static constexpr unsigned int max() { return UINT_MAX; }
55
+ unsigned int operator()() { return next_uint(); }
56
+ };
57
+ XorShift rnd_gen; // Global instance
58
+ auto G_START_TIME = std::chrono::high_resolution_clock::now();
59
+
60
+ double time_elapsed_ms() {
61
+ auto now = std::chrono::high_resolution_clock::now();
62
+ return std::chrono::duration<double, std::milli>(now - G_START_TIME).count();
63
+ }
64
+
65
+ struct AdjacencyInfo {
66
+ bool matrix[M_FIXED + 1][M_FIXED + 1];
67
+ AdjacencyInfo() {
68
+ for (int i = 0; i <= M_FIXED; ++i) for (int j = 0; j <= M_FIXED; ++j) matrix[i][j] = false;
69
+ }
70
+ void set_adj(int c1, int c2) {
71
+ if (c1 == c2) return;
72
+ matrix[std::min(c1, c2)][std::max(c1, c2)] = true;
73
+ }
74
+ bool is_adj(int c1, int c2) const {
75
+ if (c1 == c2) return false;
76
+ return matrix[std::min(c1, c2)][std::max(c1, c2)];
77
+ }
78
+ };
79
+ AdjacencyInfo required_adjacencies;
80
+ bool ward_has_any_req_adj[M_FIXED + 1];
81
+
82
+ struct BorderEdgeTracker {
83
+ int counts_arr[M_FIXED + 1][M_FIXED + 1];
84
+ BorderEdgeTracker() { clear(); }
85
+ void add_edge(int c1, int c2) {
86
+ if (c1 == c2) return;
87
+ counts_arr[std::min(c1, c2)][std::max(c1, c2)]++;
88
+ }
89
+ void remove_edge(int c1, int c2) {
90
+ if (c1 == c2) return;
91
+ counts_arr[std::min(c1, c2)][std::max(c1, c2)]--;
92
+ }
93
+ int get_count(int c1, int c2) const {
94
+ if (c1 == c2) return 0;
95
+ return counts_arr[std::min(c1, c2)][std::max(c1, c2)];
96
+ }
97
+ void clear() {
98
+ for (int i = 0; i <= M_FIXED; ++i) for (int j = 0; j <= M_FIXED; ++j) counts_arr[i][j] = 0;
99
+ }
100
+ };
101
+ BorderEdgeTracker current_border_edges_tracker;
102
+
103
+ std::vector<std::vector<std::pair<int, int>>> cells_by_color(M_FIXED + 1);
104
+ std::vector<std::vector<int>> pos_in_color_list(N_FIXED, std::vector<int>(N_FIXED));
105
+
106
+ unsigned int visited_marker_grid[N_FIXED][N_FIXED];
107
+ unsigned int current_visit_marker = 0;
108
+
109
+ std::queue<std::pair<int, int>> q_bfs_global;
110
+
111
+ const int DR[] = {-1, 1, 0, 0};
112
+ const int DC[] = {0, 0, -1, 1};
113
+
114
+ inline bool is_cell_on_grid(int r, int c) { return r >= 0 && r < N_FIXED && c >= 0 && c < N_FIXED; }
115
+
116
+ void increment_bfs_marker() {
117
+ current_visit_marker++;
118
+ if (current_visit_marker == 0) {
119
+ for (int i = 0; i < N_FIXED; ++i) {
120
+ for (int j = 0; j < N_FIXED; ++j) {
121
+ visited_marker_grid[i][j] = 0;
122
+ }
123
+ }
124
+ current_visit_marker = 1;
125
+ }
126
+ }
127
+
128
+ void clear_global_bfs_queue() {
129
+ std::queue<std::pair<int, int>> empty_queue;
130
+ std::swap(q_bfs_global, empty_queue);
131
+ }
132
+
133
+ void add_cell_to_color_ds(int r, int c, int color) {
134
+ cells_by_color[color].push_back({r,c});
135
+ pos_in_color_list[r][c] = cells_by_color[color].size() - 1;
136
+ }
137
+
138
+ void remove_cell_from_color_ds(int r, int c, int color) {
139
+ int idx_to_remove = pos_in_color_list[r][c];
140
+ std::pair<int,int> last_cell = cells_by_color[color].back();
141
+
142
+ cells_by_color[color][idx_to_remove] = last_cell;
143
+ pos_in_color_list[last_cell.first][last_cell.second] = idx_to_remove;
144
+
145
+ cells_by_color[color].pop_back();
146
+ }
147
+
148
+ void initialize_all_data_structures(const std::vector<std::vector<int>>& initial_grid) {
149
+ required_adjacencies = AdjacencyInfo();
150
+ current_border_edges_tracker.clear();
151
+ for(int i=0; i <= M_FIXED; ++i) cells_by_color[i].clear();
152
+
153
+ for (int i = 0; i < N_FIXED; ++i) {
154
+ for (int j = 0; j < N_FIXED; ++j) {
155
+ current_grid_state[i][j] = initial_grid[i][j];
156
+ add_cell_to_color_ds(i, j, initial_grid[i][j]);
157
+ }
158
+ }
159
+
160
+ for (int i = 0; i < N_FIXED; ++i) {
161
+ for (int j = 0; j < N_FIXED; ++j) {
162
+ int initial_color_val = initial_grid[i][j];
163
+ if (i == 0 || i == N_FIXED - 1 || j == 0 || j == N_FIXED - 1) {
164
+ required_adjacencies.set_adj(0, initial_color_val);
165
+ }
166
+ if (j + 1 < N_FIXED && initial_color_val != initial_grid[i][j+1]) {
167
+ required_adjacencies.set_adj(initial_color_val, initial_grid[i][j+1]);
168
+ }
169
+ if (i + 1 < N_FIXED && initial_color_val != initial_grid[i+1][j]) {
170
+ required_adjacencies.set_adj(initial_color_val, initial_grid[i+1][j]);
171
+ }
172
+
173
+ int current_color_val = current_grid_state[i][j];
174
+ if (i == 0) current_border_edges_tracker.add_edge(0, current_color_val);
175
+ if (i == N_FIXED - 1) current_border_edges_tracker.add_edge(0, current_color_val);
176
+ if (j == 0) current_border_edges_tracker.add_edge(0, current_color_val);
177
+ if (j == N_FIXED - 1) current_border_edges_tracker.add_edge(0, current_color_val);
178
+
179
+ if (j + 1 < N_FIXED && current_color_val != current_grid_state[i][j+1]) {
180
+ current_border_edges_tracker.add_edge(current_color_val, current_grid_state[i][j+1]);
181
+ }
182
+ if (i + 1 < N_FIXED && current_color_val != current_grid_state[i+1][j]) {
183
+ current_border_edges_tracker.add_edge(current_color_val, current_grid_state[i+1][j]);
184
+ }
185
+ }
186
+ }
187
+
188
+ for (int c1 = 0; c1 <= M_FIXED; ++c1) {
189
+ ward_has_any_req_adj[c1] = false;
190
+ for (int c2 = 0; c2 <= M_FIXED; ++c2) {
191
+ if (c1 == c2) continue;
192
+ if (required_adjacencies.is_adj(c1, c2)) {
193
+ ward_has_any_req_adj[c1] = true;
194
+ break;
195
+ }
196
+ }
197
+ }
198
+
199
+ best_grid_state = current_grid_state;
200
+ best_score_val = cells_by_color[0].size();
201
+ }
202
+
203
+ bool check_region_connectivity_bfs(int target_color) {
204
+ const auto& cells_of_target_color = cells_by_color[target_color];
205
+ if (cells_of_target_color.empty()) return true;
206
+
207
+ increment_bfs_marker();
208
+ clear_global_bfs_queue();
209
+
210
+ q_bfs_global.push(cells_of_target_color[0]);
211
+ visited_marker_grid[cells_of_target_color[0].first][cells_of_target_color[0].second] = current_visit_marker;
212
+
213
+ int count_visited_cells = 0;
214
+ while (!q_bfs_global.empty()) {
215
+ std::pair<int, int> curr = q_bfs_global.front();
216
+ q_bfs_global.pop();
217
+ count_visited_cells++;
218
+
219
+ for (int k = 0; k < 4; ++k) {
220
+ int nr = curr.first + DR[k];
221
+ int nc = curr.second + DC[k];
222
+ if (is_cell_on_grid(nr, nc) &&
223
+ current_grid_state[nr][nc] == target_color &&
224
+ visited_marker_grid[nr][nc] != current_visit_marker) {
225
+ visited_marker_grid[nr][nc] = current_visit_marker;
226
+ q_bfs_global.push({nr, nc});
227
+ }
228
+ }
229
+ }
230
+ return count_visited_cells == cells_of_target_color.size();
231
+ }
232
+
233
+ bool check_region_0_connectivity_full() {
234
+ const auto& cells_c0 = cells_by_color[0];
235
+ if (cells_c0.empty()) {
236
+ return true;
237
+ }
238
+
239
+ increment_bfs_marker();
240
+ clear_global_bfs_queue();
241
+
242
+ bool any_boundary_zero_cell_found = false;
243
+ for (const auto& cell_coord : cells_c0) {
244
+ int r = cell_coord.first;
245
+ int c = cell_coord.second;
246
+ if (r == 0 || r == N_FIXED - 1 || c == 0 || c == N_FIXED - 1) {
247
+ if (visited_marker_grid[r][c] != current_visit_marker) {
248
+ q_bfs_global.push(cell_coord);
249
+ visited_marker_grid[r][c] = current_visit_marker;
250
+ }
251
+ any_boundary_zero_cell_found = true;
252
+ }
253
+ }
254
+
255
+ if (!any_boundary_zero_cell_found) {
256
+ return false;
257
+ }
258
+
259
+ while (!q_bfs_global.empty()) {
260
+ std::pair<int, int> curr = q_bfs_global.front();
261
+ q_bfs_global.pop();
262
+
263
+ for (int k_dir = 0; k_dir < 4; ++k_dir) {
264
+ int nr = curr.first + DR[k_dir];
265
+ int nc = curr.second + DC[k_dir];
266
+ if (is_cell_on_grid(nr, nc) &&
267
+ current_grid_state[nr][nc] == 0 &&
268
+ visited_marker_grid[nr][nc] != current_visit_marker) {
269
+ visited_marker_grid[nr][nc] = current_visit_marker;
270
+ q_bfs_global.push({nr, nc});
271
+ }
272
+ }
273
+ }
274
+
275
+ for (const auto& cell_coord : cells_c0) {
276
+ if (visited_marker_grid[cell_coord.first][cell_coord.second] != current_visit_marker) {
277
+ return false;
278
+ }
279
+ }
280
+ return true;
281
+ }
282
+
283
+ std::map<std::pair<int, int>, int> temp_adj_deltas_map_global;
284
+
285
+ bool attempt_change_cell_color_and_validate(int r, int c, int old_color, int new_color) {
286
+ current_grid_state[r][c] = new_color;
287
+ remove_cell_from_color_ds(r, c, old_color);
288
+ add_cell_to_color_ds(r, c, new_color);
289
+
290
+ temp_adj_deltas_map_global.clear();
291
+ for (int k_adj=0; k_adj<4; ++k_adj) {
292
+ int nr = r + DR[k_adj];
293
+ int nc = c + DC[k_adj];
294
+ int neighbor_actual_color = is_cell_on_grid(nr,nc) ? current_grid_state[nr][nc] : 0;
295
+
296
+ if (old_color != neighbor_actual_color) {
297
+ temp_adj_deltas_map_global[{std::min(old_color, neighbor_actual_color), std::max(old_color, neighbor_actual_color)}]--;
298
+ }
299
+ if (new_color != neighbor_actual_color) {
300
+ temp_adj_deltas_map_global[{std::min(new_color, neighbor_actual_color), std::max(new_color, neighbor_actual_color)}]++;
301
+ }
302
+ }
303
+ for(const auto& entry : temp_adj_deltas_map_global) {
304
+ int c1 = entry.first.first; int c2 = entry.first.second; int delta = entry.second;
305
+ if (delta > 0) for(int i=0; i<delta; ++i) current_border_edges_tracker.add_edge(c1,c2);
306
+ else for(int i=0; i<-delta; ++i) current_border_edges_tracker.remove_edge(c1,c2);
307
+ }
308
+
309
+ bool is_change_valid = true;
310
+
311
+ for(const auto& entry : temp_adj_deltas_map_global) {
312
+ int c1 = entry.first.first; int c2 = entry.first.second;
313
+ bool has_edge_now = current_border_edges_tracker.get_count(c1, c2) > 0;
314
+ bool needs_edge = required_adjacencies.is_adj(c1, c2);
315
+ if (has_edge_now != needs_edge) {
316
+ is_change_valid = false; break;
317
+ }
318
+ }
319
+
320
+ if (is_change_valid && old_color != 0 && cells_by_color[old_color].empty() && ward_has_any_req_adj[old_color]) {
321
+ is_change_valid = false;
322
+ }
323
+
324
+ if (is_change_valid && old_color != 0 && !cells_by_color[old_color].empty()) {
325
+ if (!check_region_connectivity_bfs(old_color)) is_change_valid = false;
326
+ }
327
+
328
+ if (is_change_valid && new_color != 0) {
329
+ if (!check_region_connectivity_bfs(new_color)) is_change_valid = false;
330
+ }
331
+
332
+ if (is_change_valid && (old_color == 0 || new_color == 0)) {
333
+ if (!cells_by_color[0].empty()) {
334
+ if (!check_region_0_connectivity_full()) is_change_valid = false;
335
+ } else {
336
+ if (ward_has_any_req_adj[0]) {
337
+ is_change_valid = false;
338
+ }
339
+ }
340
+ }
341
+
342
+ if (!is_change_valid) {
343
+ current_grid_state[r][c] = old_color;
344
+ remove_cell_from_color_ds(r, c, new_color);
345
+ add_cell_to_color_ds(r, c, old_color);
346
+
347
+ for(const auto& entry : temp_adj_deltas_map_global) {
348
+ int c1_ = entry.first.first; int c2_ = entry.first.second; int delta = entry.second;
349
+ if (delta > 0) for(int i=0; i<delta; ++i) current_border_edges_tracker.remove_edge(c1_,c2_);
350
+ else for(int i=0; i<-delta; ++i) current_border_edges_tracker.add_edge(c1_,c2_);
351
+ }
352
+ return false;
353
+ }
354
+ return true;
355
+ }
356
+
357
+ void solve_main_logic() {
358
+ std::vector<std::vector<int>> initial_grid_from_input(N_FIXED, std::vector<int>(N_FIXED));
359
+ for (int i = 0; i < N_FIXED; ++i) for (int j = 0; j < N_FIXED; ++j) std::cin >> initial_grid_from_input[i][j];
360
+
361
+ initialize_all_data_structures(initial_grid_from_input);
362
+
363
+ const double GREEDY_PASS_BUDGET_MS = 300.0;
364
+ double greedy_pass_start_abs_time = time_elapsed_ms();
365
+
366
+ std::vector<std::pair<int,int>> all_cells_shuffled;
367
+ all_cells_shuffled.reserve(N_FIXED * N_FIXED);
368
+ for(int r_idx=0; r_idx<N_FIXED; ++r_idx) for(int c_idx=0; c_idx<N_FIXED; ++c_idx) all_cells_shuffled.push_back({r_idx,c_idx});
369
+
370
+ std::shuffle(all_cells_shuffled.begin(), all_cells_shuffled.end(), rnd_gen);
371
+ for (const auto& cell_coords : all_cells_shuffled) {
372
+ if (time_elapsed_ms() - greedy_pass_start_abs_time > GREEDY_PASS_BUDGET_MS) break;
373
+
374
+ int r = cell_coords.first; int c = cell_coords.second;
375
+ int original_color = current_grid_state[r][c];
376
+ if (original_color == 0) continue;
377
+
378
+ if (attempt_change_cell_color_and_validate(r, c, original_color, 0)) {
379
+ int current_zeros_count = cells_by_color[0].size();
380
+ if (current_zeros_count > best_score_val) {
381
+ best_score_val = current_zeros_count;
382
+ best_grid_state = current_grid_state;
383
+ }
384
+ }
385
+ }
386
+
387
+ double sa_start_temp = 2.0;
388
+ double sa_end_temp = 0.01;
389
+ const double TOTAL_COMPUTATION_TIME_MS = 1950.0;
390
+
391
+ double sa_start_abs_time = time_elapsed_ms();
392
+ double sa_total_duration_ms = TOTAL_COMPUTATION_TIME_MS - sa_start_abs_time;
393
+ if (sa_total_duration_ms <= 0) sa_total_duration_ms = 1.0;
394
+
395
+ int iter_count = 0;
396
+ while(true) {
397
+ iter_count++;
398
+ if(iter_count % 256 == 0) {
399
+ if (time_elapsed_ms() >= TOTAL_COMPUTATION_TIME_MS) break;
400
+ }
401
+
402
+ double time_spent_in_sa = time_elapsed_ms() - sa_start_abs_time;
403
+ double progress_ratio = (sa_total_duration_ms > 1e-9) ? (time_spent_in_sa / sa_total_duration_ms) : 1.0;
404
+ progress_ratio = std::min(progress_ratio, 1.0);
405
+
406
+ double current_temperature = sa_start_temp * std::pow(sa_end_temp / sa_start_temp, progress_ratio);
407
+ current_temperature = std::max(current_temperature, sa_end_temp);
408
+
409
+ int r_coord = rnd_gen.next_int(N_FIXED);
410
+ int c_coord = rnd_gen.next_int(N_FIXED);
411
+ int original_color_at_cell = current_grid_state[r_coord][c_coord];
412
+
413
+ int candidate_new_colors[5];
414
+ int num_candidate_options = 0;
415
+ candidate_new_colors[num_candidate_options++] = 0;
416
+ for(int k_neighbor_idx=0; k_neighbor_idx<4; ++k_neighbor_idx) {
417
+ int nr = r_coord + DR[k_neighbor_idx];
418
+ int nc = c_coord + DC[k_neighbor_idx];
419
+ if (is_cell_on_grid(nr,nc)) {
420
+ candidate_new_colors[num_candidate_options++] = current_grid_state[nr][nc];
421
+ } else {
422
+ candidate_new_colors[num_candidate_options++] = 0;
423
+ }
424
+ }
425
+ int new_proposed_color = candidate_new_colors[rnd_gen.next_int(num_candidate_options)];
426
+
427
+ if (original_color_at_cell == new_proposed_color) continue;
428
+
429
+ int delta_in_score_metric = 0;
430
+ if (new_proposed_color == 0 && original_color_at_cell != 0) delta_in_score_metric = 1;
431
+ else if (new_proposed_color != 0 && original_color_at_cell == 0) delta_in_score_metric = -1;
432
+
433
+ if (attempt_change_cell_color_and_validate(r_coord, c_coord, original_color_at_cell, new_proposed_color)) {
434
+ bool accept_this_move = false;
435
+ if (delta_in_score_metric >= 0) {
436
+ accept_this_move = true;
437
+ if (cells_by_color[0].size() > best_score_val) {
438
+ best_score_val = cells_by_color[0].size();
439
+ best_grid_state = current_grid_state;
440
+ }
441
+ } else {
442
+ if (current_temperature > 1e-9 && rnd_gen.next_double() < std::exp((double)delta_in_score_metric / current_temperature)) {
443
+ accept_this_move = true;
444
+ } else {
445
+ accept_this_move = false;
446
+ }
447
+ }
448
+
449
+ if (!accept_this_move) {
450
+ current_grid_state[r_coord][c_coord] = original_color_at_cell;
451
+ remove_cell_from_color_ds(r_coord, c_coord, new_proposed_color);
452
+ add_cell_to_color_ds(r_coord, c_coord, original_color_at_cell);
453
+
454
+ for(const auto& entry : temp_adj_deltas_map_global) {
455
+ int c1_ = entry.first.first; int c2_ = entry.first.second; int delta = entry.second;
456
+ if (delta > 0) for(int i=0; i<delta; ++i) current_border_edges_tracker.remove_edge(c1_,c2_);
457
+ else for(int i=0; i<-delta; ++i) current_border_edges_tracker.add_edge(c1_,c2_);
458
+ }
459
+ }
460
+ }
461
+ }
462
+
463
+ for (int i = 0; i < N_FIXED; ++i) {
464
+ for (int j = 0; j < N_FIXED; ++j) {
465
+ std::cout << best_grid_state[i][j] << (j == N_FIXED - 1 ? "" : " ");
466
+ }
467
+ std::cout << std::endl;
468
+ }
469
+ }
470
+
471
+ int main() {
472
+ std::ios_base::sync_with_stdio(false); std::cin.tie(NULL);
473
+ G_START_TIME = std::chrono::high_resolution_clock::now();
474
+
475
+ int n_in_dummy, m_in_dummy;
476
+ std::cin >> n_in_dummy >> m_in_dummy;
477
+
478
+ solve_main_logic();
479
+ return 0;
480
+ }
481
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc025/best_program.cpp ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <bits/stdc++.h>
3
+ using namespace std;
4
+
5
+ /* Approach overview:
6
+ - Sort k pivots by 1v1 balance queries (merge sort), k is a power of two under budget.
7
+ - Classify non-pivots by binary search among pivots.
8
+ - Assign surrogate weights from ranks (spread quadratically for separation).
9
+ - Greedy LPT packing, then a tiny deterministic refinement (move/swap) to reduce sum of squares.
10
+ - Use remaining queries (if any) for light interactive refinement between groups, then pad. */
11
+
12
+ // Global parameters and query counter
13
+ int N_, D_, Q_;
14
+ int q_used = 0;
15
+
16
+ // Cache for 1v1 comparisons (symmetry-aware)
17
+ /* cmp(a,b):
18
+ - Returns '<' if a is lighter than b, '>' if heavier, '=' if equal.
19
+ - Uses a 1 item per pan query and memoizes symmetric results. */
20
+ static unsigned char memo_cmp[128][128]; // 0=unknown; else '<','>','='
21
+
22
+ // Perform actual query
23
+ /* ask(L,R): prints the sets on the pan and reads the judge's response.
24
+ Requirements: both sides non-empty and disjoint; used for both 1v1 and set vs set. */
25
+ char ask(const vector<int>& L, const vector<int>& R){ /* Query balance between sets L and R; returns '<','>','='. Flush each call. */
26
+ ++q_used;
27
+ cout << (int)L.size() << " " << (int)R.size();
28
+ for(int x: L) cout << " " << x;
29
+ for(int x: R) cout << " " << x;
30
+ cout << endl;
31
+ char c; cin >> c;
32
+ return c;
33
+ }
34
+
35
+ // Compare two single items with caching
36
+ /* cmp(a,b) as comparator:
37
+ - If we ran out of queries, conservatively returns '=' to avoid TLE/illegal ops. */
38
+ char cmp(int a, int b){ /* Compare singletons a vs b with memoization; returns '<','>','='. */
39
+ if(a==b) return '=';
40
+ if(memo_cmp[a][b]) return (char)memo_cmp[a][b];
41
+ if(q_used>=Q_) return '=';
42
+ char r = ask({a},{b});
43
+ memo_cmp[a][b] = (unsigned char)r;
44
+ memo_cmp[b][a] = (unsigned char)(r=='<'?'>':(r=='>'?'<':'='));
45
+ return r;
46
+ }
47
+
48
+ // Merge sort using cmp as comparator on item indices
49
+ /* merge_sort(ids,l,r):
50
+ - Sorts ids[l..r] in non-decreasing order of weight using only cmp(). */
51
+ void merge_sort(vector<int>& ids, int l, int r){ /* Stable merge sort using cmp() as comparator over indices. */
52
+ if(l>=r) return;
53
+ int m=(l+r)>>1;
54
+ merge_sort(ids,l,m);
55
+ merge_sort(ids,m+1,r);
56
+ vector<int> tmp; tmp.reserve(r-l+1);
57
+ int i=l,j=m+1;
58
+ while(i<=m && j<=r){
59
+ char c = cmp(ids[i], ids[j]);
60
+ // Treat '=' as <= to keep order deterministic
61
+ if(c=='<' || c=='=') tmp.push_back(ids[i++]);
62
+ else tmp.push_back(ids[j++]);
63
+ }
64
+ while(i<=m) tmp.push_back(ids[i++]);
65
+ while(j<=r) tmp.push_back(ids[j++]);
66
+ for(int k=0;k<(int)tmp.size();++k) ids[l+k]=tmp[k];
67
+ }
68
+
69
+ int main(){ /* Orchestrates: budgeted ranking via 1v1, surrogate weights from exp order stats, LPT pack, local+interactive refinement. */
70
+ ios::sync_with_stdio(false);
71
+ cin.tie(nullptr);
72
+
73
+ cin >> N_ >> D_ >> Q_;
74
+ // Choose pivots with a reserved budget for later set-based refinement.
75
+ // With k=2^e, mergesort on pivots costs ~k*e and classifying others costs ~(N-k)*e => ~N*e.
76
+ int log2N = 0; while((1<<(log2N+1))<=max(1,N_)) ++log2N;
77
+ int reserve = max(D_, Q_/4);
78
+ int e = min(log2N, max(1, (Q_ - reserve - 2)/max(1,N_)));
79
+ int k = min(N_, 1<<e);
80
+ int e_full = 0; while((1<<e_full) < max(1,N_)) ++e_full;
81
+ long long cost_full = 1LL * N_ * e_full;
82
+ if(Q_ - reserve - 2 >= cost_full) k = N_;
83
+ if(k<1) k=1;
84
+
85
+ // Pick first k items as pivots and sort them by weight
86
+ vector<int> piv(k);
87
+ iota(piv.begin(), piv.end(), 0);
88
+ merge_sort(piv, 0, k-1);
89
+
90
+ vector<char> is_pivot(N_, 0);
91
+ for(int x: piv) is_pivot[x]=1;
92
+
93
+ // Surrogate weights via expected order statistics of exponential distribution.
94
+ // Map rank quantile q in (0,1] to w_hat ∝ H_N - H_{N - round(q*N)}, then scale to integers.
95
+ vector<long double> H(N_+1, 0.0L);
96
+ for(int i=1;i<=N_;++i) H[i]=H[i-1]+1.0L/i;
97
+ long double HN = H[N_];
98
+ auto q_to_w = [&](long double q)->long long{
99
+ if(q<=0) return 1;
100
+ int idx = (int)llround(q * (long double)N_);
101
+ if(idx<1) idx=1; if(idx>N_) idx=N_;
102
+ long double v = HN - H[N_-idx];
103
+ long long w = (long long)llround(v * 1000000.0L);
104
+ if(w<1) w=1;
105
+ return w;
106
+ };
107
+ vector<long long> w_hat(N_, 1);
108
+ for(int i=0;i<k;i++){
109
+ long double q = ((long double)i + 0.5L) / (long double)k;
110
+ w_hat[piv[i]] = q_to_w(q);
111
+ }
112
+
113
+ // Classify non-pivot items by binary searching among pivots
114
+ for(int id=0; id<N_; ++id){
115
+ if(is_pivot[id]) continue;
116
+ int lo=0, hi=k;
117
+ while(lo<hi){
118
+ int mid=(lo+hi)/2;
119
+ char r = cmp(id, piv[mid]);
120
+ if(r=='<' || r=='=') hi=mid; else lo=mid+1;
121
+ }
122
+ long double q;
123
+ if(lo==0) q = 0.25L / max(1, k); // slightly above 0
124
+ else if(lo==k) q = ((long double)k - 0.25L) / (long double)k; // slightly below 1
125
+ else q = ((long double)lo) / (long double)k;
126
+ w_hat[id] = q_to_w(q);
127
+ }
128
+
129
+ // Greedy largest-first with tie-breaker by size
130
+ vector<pair<long long,int>> ord; ord.reserve(N_);
131
+ for(int i=0;i<N_;++i) ord.push_back({-w_hat[i], i});
132
+ sort(ord.begin(), ord.end());
133
+ vector<long long> sum(D_, 0);
134
+ vector<vector<int>> grp(D_);
135
+ vector<int> ans(N_, 0);
136
+ for(auto [negw, id] : ord){
137
+ int best = 0;
138
+ for(int g=1; g<D_; ++g){
139
+ if(sum[g] < sum[best] || (sum[g]==sum[best] && grp[g].size() < grp[best].size())) best=g;
140
+ }
141
+ ans[id] = best;
142
+ sum[best] += -negw;
143
+ grp[best].push_back(id);
144
+ }
145
+
146
+ // Deterministic local refinement: move or swap between heaviest and lightest if it improves sum of squares
147
+ {
148
+ int iter_limit = max(100, N_);
149
+ for(int it=0; it<iter_limit; ++it){
150
+ int gH=0,gL=0;
151
+ for(int g=1; g<D_; ++g){ if(sum[g]>sum[gH]) gH=g; if(sum[g]<sum[gL]) gL=g; }
152
+ if(gH==gL) break;
153
+ long long SA=sum[gH], SB=sum[gL], diff=SA-SB;
154
+ if(diff<=0) break;
155
+
156
+ // Try a single-item move from gH to gL
157
+ int bestId=-1; long long bestGap=(1LL<<62);
158
+ for(int id: grp[gH]){
159
+ long long w=w_hat[id];
160
+ if(w<diff){
161
+ long long gap = llabs((long long)(diff/2) - w);
162
+ if(gap<bestGap){ bestGap=gap; bestId=id; }
163
+ }
164
+ }
165
+ bool improved=false;
166
+ if(bestId!=-1){
167
+ long long w=w_hat[bestId];
168
+ long long nSA=SA-w, nSB=SB+w;
169
+ long long old2=SA*SA + SB*SB, new2=nSA*nSA + nSB*nSB;
170
+ if(new2<old2){
171
+ sum[gH]=nSA; sum[gL]=nSB;
172
+ auto &A=grp[gH]; auto &B=grp[gL];
173
+ for(int i=0;i<(int)A.size();++i) if(A[i]==bestId){ A[i]=A.back(); A.pop_back(); break; }
174
+ B.push_back(bestId);
175
+ improved=true;
176
+ }
177
+ }
178
+ if(improved) continue;
179
+
180
+ // Try swapping one item between gH and gL
181
+ int bestA=-1, bestB=-1; bestGap=(1LL<<62);
182
+ for(int ia: grp[gH]){
183
+ long long wA=w_hat[ia];
184
+ for(int ib: grp[gL]){
185
+ long long wB=w_hat[ib];
186
+ long long delta = wA - wB;
187
+ if(delta<=0 || delta>=diff) continue;
188
+ long long gap = llabs((long long)(diff/2) - delta);
189
+ if(gap<bestGap){ bestGap=gap; bestA=ia; bestB=ib; }
190
+ }
191
+ }
192
+ if(bestA!=-1){
193
+ long long wA=w_hat[bestA], wB=w_hat[bestB];
194
+ long long nSA=SA - wA + wB, nSB=SB - wB + wA;
195
+ long long old2=SA*SA + SB*SB, new2=nSA*nSA + nSB*nSB;
196
+ if(new2<old2){
197
+ sum[gH]=nSA; sum[gL]=nSB;
198
+ auto &A=grp[gH]; auto &B=grp[gL];
199
+ for(int i=0;i<(int)A.size();++i) if(A[i]==bestA){ A[i]=bestB; break; }
200
+ for(int i=0;i<(int)B.size();++i) if(B[i]==bestB){ B[i]=bestA; break; }
201
+ continue;
202
+ }
203
+ }
204
+ break; // no improving move or swap
205
+ }
206
+ }
207
+
208
+ // Rebuild final assignment from groups (keeps consistency after refinement)
209
+ fill(ans.begin(), ans.end(), 0);
210
+ for(int g=0; g<D_; ++g) for(int id: grp[g]) ans[id]=g;
211
+
212
+ // Set-based interactive refinement using remaining queries: move single items guided by balance
213
+ {
214
+ if(q_used < Q_){
215
+ mt19937 rng(712367);
216
+ vector<pair<int,int>> pairs;
217
+ pairs.reserve(D_*(D_-1)/2);
218
+ for(int a=0;a<D_;++a) for(int b=a+1;b<D_;++b) pairs.emplace_back(a,b);
219
+ int rem = Q_ - q_used;
220
+ int passes = min(24, 2 + rem / max(1, D_));
221
+ int K = min(12, 2 + rem / max(1, D_));
222
+ for(int pass=0; pass<passes && q_used < Q_; ++pass){
223
+ shuffle(pairs.begin(), pairs.end(), rng);
224
+ bool any=false;
225
+ for(auto pr: pairs){
226
+ int a=pr.first, b=pr.second;
227
+ if(q_used >= Q_) break;
228
+ if(grp[a].empty() || grp[b].empty()) continue;
229
+ char r = ask(grp[a], grp[b]);
230
+ if(r=='=') continue;
231
+ int H = (r=='>')?a:b;
232
+ int L = (H==a)?b:a;
233
+ if((int)grp[H].size()<=1) continue;
234
+ vector<int> idx(grp[H].size()); iota(idx.begin(), idx.end(), 0);
235
+ shuffle(idx.begin(), idx.end(), rng);
236
+ int tries = min(K, (int)idx.size());
237
+ for(int t=0; t<tries && q_used < Q_; ++t){
238
+ int pos = idx[t];
239
+ int id = grp[H][pos];
240
+ // Build H\{id} as left set
241
+ vector<int> Left; Left.reserve(grp[H].size()-1);
242
+ for(int x: grp[H]) if(x!=id) Left.push_back(x);
243
+ if(Left.empty() || grp[L].empty()) continue;
244
+ char rr = ask(Left, grp[L]);
245
+ if(rr=='>'){
246
+ // apply move id: H -> L; keep surrogate sums in sync
247
+ grp[L].push_back(id);
248
+ grp[H][pos] = grp[H].back(); grp[H].pop_back();
249
+ sum[H] -= w_hat[id];
250
+ sum[L] += w_hat[id];
251
+ any=true;
252
+ break;
253
+ }
254
+ }
255
+ }
256
+ if(!any) break;
257
+ }
258
+ }
259
+ }
260
+
261
+ // Rebuild final assignment from groups after interactive refinement
262
+ fill(ans.begin(), ans.end(), 0);
263
+ for(int g=0; g<D_; ++g) for(int id: grp[g]) ans[id]=g;
264
+
265
+ // Consume any remaining queries with safe 1v1 dummies
266
+ if(N_>=2){
267
+ int a=0, b=1;
268
+ while(q_used < Q_){
269
+ ask({a},{b});
270
+ b = (b+1)%N_;
271
+ if(b==a) b=(b+1)%N_;
272
+ }
273
+ }
274
+
275
+ for(int i=0;i<N_;++i){
276
+ if(i) cout << ' ';
277
+ cout << ans[i];
278
+ }
279
+ cout << '\n';
280
+ return 0;
281
+ }
282
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc025/config.yaml ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ALE-Bench ahc025 — AtCoder Heuristic Contest
2
+ # Usage: skydiscover-run initial_program.cpp evaluator.py -c config.yaml -s <strategy>
3
+ language: cpp
4
+ diff_based_generation: true
5
+ max_iterations: 100
6
+ checkpoint_interval: 10
7
+ max_solution_length: 60000
8
+ llm:
9
+ api_base: https://api.openai.com/v1
10
+ models:
11
+ - name: "gpt-5"
12
+ weight: 1.0
13
+ max_tokens: 32000
14
+ timeout: 600
15
+ prompt:
16
+ system_message: "You are a world-class algorithm engineer, and you are very good at programming. Now, you are participating\
17
+ \ in a programming contest. You are asked to solve a heuristic problem, known as an NP-hard problem.\n \n Story\n\
18
+ --------\nAtCoder offers online shopping for official goods.\nCEO Takahashi decided to sell the unsold goods together\
19
+ \ as a grab bag.\nThe weight of each bag should be as even as possible, but unfortunately, there was no scale in AtCoder's\
20
+ \ office to measure weight numerically.\nAs an alternative, he found a balance.\nBy using the balance to compare the weights,\
21
+ \ please divide the goods as evenly as possible.\n\nProblem Statement\n--------\nThere are $N$ items.\nThe weight $w_i$\
22
+ \ of each item $i$ is unknown.\nUsing a balance that can compare the sum of the weights of two item sets, you repeat the\
23
+ \ following operations.\n\nPlace as many items as you like on the left and right plates of the balance. Then you can see\
24
+ \ which side has the greater weight or they have equal weights.\n\nAfter repeating this operation $Q$ times, divide the\
25
+ \ items into $D$ sets of equal total weight as much as possible.\n\n\nScoring\n--------\nLet $t_0,t_1,\\cdots,t_{D-1}$\
26
+ \ be the total weight of items in each set in the output division. Let the mean of $t$ be $\\bar{t}=\\frac{1}{D}\\sum_{i=0}^{D-1}t_i$.\
27
+ \ The variance is $V=\\frac{1}{D}\\sum_{i=0}^{D-1} (t_i-\\bar{t})^2$.\nThen you will obtain an absolute score of $1+\\\
28
+ mathrm{round}\\left(100\\times \\sqrt{V}\\right)$.\nThe lower the absolute score, the better.\n\nFor each test case, you\
29
+ \ will obtain a <font color=\"red\"><strong>rank score</strong></font> according to your rank determined by lower absolute\
30
+ \ score. The score of the submission is the total rank score for each test case. The rank score is calculated as follows,\
31
+ \ and the higher the rank score, the better.\n\nLet $n_{submit}$ be the number of contestants with submissions, $n_{lose}$\
32
+ \ be the number of contestants who received an absolute score lower than yours, and $n_{tie}$ be the number of other contestants\
33
+ \ who received an absolute score equal to yours. Then your rank in this test case is determined as $r=n_{lose}+0.5 n_{tie}$,\
34
+ \ and your rank score is $\\mathrm{round}(10^8\\times (1-\\frac{r}{n_{submit}}))$.\n\n\nThe final ranking will be determined\
35
+ \ by the system test with more inputs which will be run after the contest is over.\nIn both the provisional/system test,\
36
+ \ if your submission produces illegal output or exceeds the time limit for some test cases, only the rank score for those\
37
+ \ test cases will be zero.\nThe system test will be performed only for <font color=\"red\"><strong>the last submission\
38
+ \ which received a result other than <span class=\"label label-warning\" data-toggle=\"tooltip\" data-placement=\"top\"\
39
+ \ title=\"\" data-original-title=\"Compilation Error\">CE</span> </strong></font>.\nBe careful not to make a mistake in\
40
+ \ the final submission.\n\n#### Number of test cases\n- Provisional test: 100\n- System test: 5000. We will publish <a\
41
+ \ href=\"https://img.atcoder.jp/ahc025/seeds.txt\">seeds.txt</a> (sha256=8a39261299bef0387172c0e0c4523c49b0cb993efd4f702ec7cf5124cf5b4c55)\
42
+ \ after the contest is over.\n\n\n#### About relative evaluation system\nIn both the provisional/system test, the standings\
43
+ \ will be calculated using only the last submission which received a result other than <span class=\"label label-warning\"\
44
+ \ data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Compilation Error\">CE</span>.\n\nThe\
45
+ \ scores shown in the standings are relative, and whenever a new submission arrives, all relative scores are recalculated.\n\
46
+ On the other hand, the score for each submission shown on the submissions page is the sum of the absolute score for each\
47
+ \ test case, and the relative scores are not shown.\nIn order to know the relative score of submission other than the\
48
+ \ latest one in the current standings, you need to resubmit it.\nIf your submission produces illegal output or exceeds\
49
+ \ the time limit for some test cases, the score shown on the submissions page will be 0, but the standings show the sum\
50
+ \ of the relative scores for the test cases that were answered correctly.\n\n#### About execution time\nExecution time\
51
+ \ may vary slightly from run to run.\nIn addition, since system tests simultaneously perform a large number of executions,\
52
+ \ it has been observed that execution time increases by several percent compared to provisional tests.\nFor these reasons,\
53
+ \ submissions that are very close to the time limit may result in <span class='label label-warning' data-toggle='tooltip'\
54
+ \ data-placement='top' title=\"Time Limit Exceeded\">TLE</span> in the system test.\nPlease measure the execution time\
55
+ \ in your program to terminate the process, or have enough margin in the execution time.\n\n\nInput and Output\n--------\n\
56
+ First, the number of items $N$, the number of divisions $D$, and the number of queries $Q$ are given from Standard Input\
57
+ \ in the following format.\n~~~\n$N$ $D$ $Q$\n~~~\n\nEach value satisfies the following constraints.\n\n- $30\\leq N\\\
58
+ leq 100$\n- $2\\leq D\\leq N/4$\n- $2N\\leq Q\\leq 32N$\n\n\nAfter reading the above information, repeat the following\
59
+ \ query $Q$ times.\n\nIn the $q$-th query ($0\\leq q\\leq Q-1$), you select the set of items $L$ to be placed on the left\
60
+ \ side of the balance and the set of items $R$ to be placed on the right side of the balance.\nEach set must not be empty,\
61
+ \ and the common part $L\\cap R$ must be empty.\nThere may be items that are not contained in either $L$ or $R$.\nLet\
62
+ \ $n_L=|L|$, $n_R=|R|$, $L=\\\\{l_0,\\cdots,l_{n_L-1}\\\\}$, and $R=\\\\{r_0,\\cdots,r_{n_R-1}\\\\}$ ($0\\leq l_i,r_i\\\
63
+ leq N-1$).\nThen output to Standard Output on a single line in the following format.\n~~~\n$n_L$ $n_R$ $l_0$ $\\cdots$\
64
+ \ $l_{n_L-1}$ $r_0$ $\\cdots$ $r_{n_R-1}$\n~~~\n\n<font color=\"red\">**The output must be followed by a new line, and\
65
+ \ you have to flush Standard Output.**</font>\nOtherwise, the submission might be judged as <span class='label label-warning'\
66
+ \ data-toggle='tooltip' data-placement='top' title=\"Time Limit Exceeded\">TLE</span>.\n\nAfter output, the information\
67
+ \ on which side the balance is tilted is given in a single line from the standard input.\nThe given string is one of the\
68
+ \ following three cases.\n\n- `<` : The total weight of $L$ is less than the total weight of $R$.\n- `>` : The total weight\
69
+ \ of $L$ is greater than the total weight of $R$.\n- `=` : The total weight of $L$ is equal to the total weight of $R$.\n\
70
+ \nThe query must be performed exactly $Q$ times.\nAfter $Q$ queries, divide the items into $D$ sets of equal total weight\
71
+ \ as much as possible.\nLet $i$-th ($0\\leq i\\leq N-1$) item be included in $d_i$-th ($0\\leq d_i\\leq D-1$) set.\nThen\
72
+ \ output to Standard Output on a single line in the following format.\n~~~\n$d_0$ $\\cdots$ $d_{N-1}$\n~~~\n\n\n#### Example\n\
73
+ \n\n<table class=\"table table-bordered\">\n<thead>\n<tr>\n<th>$q$</th>\n<th>Output</th>\n<th>Input</th>\n</tr>\n</thead>\n\
74
+ <tbody>\n<tr>\n<td>Prior information</td>\n<td></td>\n<td><pre>31 2 128</pre></td>\n</tr>\n<tr>\n<td>0</td>\n<td><pre>2\
75
+ \ 1 6 22 11</pre></td>\n<td><pre>></pre></td>\n</tr>\n<tr>\n<td>1</td>\n<td><pre>2 2 11 22 0 1</pre></td>\n<td><pre><</pre></td>\n\
76
+ </tr>\n<tr>\n<td>$\\vdots$</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>127</td>\n<td><pre>1 1 14 24</pre></td>\n<td><pre><</pre></td>\n\
77
+ </tr>\n<tr>\n<td>Division</td>\n<td><pre>0 0 0 0 0 0 1 1 1 0 1 1 1 0 0 0 1 1 0 0 0 0 0 1 1 1 0 0 0 1 0</pre></td>\n<td></td>\n\
78
+ </tr>\n</tbody>\n</table>\n\n\n<a href=\"https://img.atcoder.jp/ahc025/tNvZmDfV.html?lang=en&seed=0&output=sample\">Show\
79
+ \ example</a>\n\nInput Generation\n--------\nLet $\\mathrm{rand\\\\_int}(L,U)$ be a function that generates a uniform\
80
+ \ random integer between $L$ and $U$, inclusive.\nLet $\\mathrm{rand\\\\_double}(L,U)$ be a function that generates a\
81
+ \ uniform random real number at least $L$ and less than $U$.\n\nThe number of items $N$ is generated by $\\mathrm{rand\\\
82
+ \\_int}(30,100)$.\nThe number of divisions $D$ is generated by $\\mathrm{rand\\\\_int}(2,\\mathrm{floor}(N/4))$.\nThe\
83
+ \ number of queries $Q$ is generated by $\\mathrm{round}(N\\times 2^{\\mathrm{rand\\\\_double(1,5)}})$.\n\nFor each item\
84
+ \ $i$, we independently generate a value $w'_i$ from the <a href=\"https://en.wikipedia.org/wiki/Exponential_distribution\"\
85
+ >exponential distribution</a> with $\\lambda=10^{-5}$, and we set the weight of item $i$ by $w_i=\\max(1, \\mathrm{round}(w'_i))$.\
86
+ \ If the generated value $w'_i$ exceeds $\\frac{10^5 N}{D}$, we regenerate it.\n\n\nTools (Input generator, local tester\
87
+ \ and visualizer)\n--------\n- <a href=\"https://img.atcoder.jp/ahc025/tNvZmDfV.html?lang=en\">Web version</a>: This is\
88
+ \ more powerful than the local version providing animations.\n- <a href=\"https://img.atcoder.jp/ahc025/tNvZmDfV.zip\"\
89
+ >Local version</a>: You need a compilation environment of <a href=\"https://www.rust-lang.org/\">Rust language</a>.\n\t\
90
+ - <a href=\"https://img.atcoder.jp/ahc025/tNvZmDfV_windows.zip\">Pre-compiled binary for Windows</a>: If you are not familiar\
91
+ \ with the Rust language environment, please use this instead.\n\n<font color=\"red\"><b>Please be aware that sharing\
92
+ \ visualization results or discussing solutions/ideas during the contest is prohibited.</b></font>\n\n#### Specification\
93
+ \ of input/output files used by the tools\n\nInput files given to the local tester have the following format.\n~~~\n$N$\
94
+ \ $D$ $Q$\n$w_0$ $\\cdots$ $w_{N-1}$\n~~~\nThe last $w_0$ $\\cdots$ $w_{N-1}$ is the weight of each item and is not given\
95
+ \ to the solution program.\n\nThe local tester writes outputs from your program directly to the output file.\nYour program\
96
+ \ may output comment lines starting with `#`. The web version of the visualizer displays the comment lines with the corresponding\
97
+ \ query, which may be useful for debugging and analysis. Since the judge program ignores all comment lines, you can submit\
98
+ \ a program that outputs comment lines as is.\nIn addition, comment lines starting with `#c` are treated specially and\
99
+ \ you can provide the visualizer with a tentative division by outputting in the following format.\n~~~\n#c $d_0$ $\\cdots$\
100
+ \ $d_{N-1}$\n~~~\n\n\n Problem constraints:\n time_limit=2.0 memory_limit=1073741824\n"
101
+ evaluator:
102
+ timeout: 10000
103
+ cascade_evaluation: false
104
+
benchmarks/ale_bench/ale-bench-lite-problems/ahc025/evaluator.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+ from pathlib import Path
3
+ from ale_bench.result import CaseResult, JudgeResult, Result
4
+ from ale_bench_eval.safe_ale_session import start_ale_bench_session
5
+ import logging
6
+ import sys
7
+ logger = logging.getLogger(__name__ + "_" + "ALE_BENCH_EVALUATOR")
8
+
9
+ def result_feedback(result: Result) -> CaseResult:
10
+ if result.overall_judge_result == JudgeResult.ACCEPTED:
11
+ return result.case_results[0]
12
+ else:
13
+ selected_case_idx = 0
14
+ for idx, case_result in enumerate(result.case_results):
15
+ if case_result.judge_result == result.overall_judge_result:
16
+ selected_case_idx = idx
17
+ break
18
+ return result.case_results[selected_case_idx]
19
+
20
+ def evaluate(program_path):
21
+ problem_id = "ahc025"
22
+ logger.info(f"Evaluating program {program_path} for problem {problem_id} in ale bench evaluator")
23
+ try:
24
+ session = None
25
+ logger.info("Starting ALE-Bench session")
26
+ session = start_ale_bench_session(
27
+ problem_id=problem_id,
28
+ lite_version=True,
29
+ num_workers=13,
30
+ )
31
+ logger.info("ALE-Bench session started")
32
+ if not session:
33
+ raise RuntimeError("Failed to start or restart the session.")
34
+ optim_factor = 1 if session.problem.metadata.score_type == "maximize" else -1
35
+ code = Path(program_path).read_text().replace("# EVOLVE-BLOCK-START", "").replace("# EVOLVE-BLOCK-END", "").strip()
36
+ logger.info("Code extracted")
37
+ num_public_cases = 50
38
+ cases = session.case_gen(list(range(num_public_cases)))
39
+ public_result = session.case_eval(
40
+ cases, code, code_language="cpp20", skip_local_visualization=True
41
+ )
42
+ logger.info("Public evaluation completed")
43
+ extracted_case = result_feedback(public_result)
44
+ logger.info("Result feedback completed")
45
+ logger.info("ALE-Bench session closed")
46
+ combined_score = public_result.overall_absolute_score * optim_factor / num_public_cases
47
+ if public_result.overall_judge_result != JudgeResult.ACCEPTED and optim_factor == -1:
48
+ combined_score = -sys.maxsize - 1
49
+ session.close()
50
+ return {
51
+ "judge_result": public_result.overall_judge_result.value,
52
+ "overall_score": public_result.overall_absolute_score,
53
+ "max_execution_time_sec": max([case_result.execution_time for case_result in public_result.case_results]),
54
+ "max_memory_usage_mib": max([case_result.memory_usage for case_result in public_result.case_results]) // 1024 // 1024,
55
+ "standard_error": extracted_case.error_str,
56
+ "message": extracted_case.message,
57
+ "combined_score": combined_score,
58
+ }
59
+ except Exception as e:
60
+ logger.error(f"Evaluation failed completely: {str(e)}")
61
+ logger.error(traceback.format_exc())
62
+ return {
63
+ "overall_score": 0.0,
64
+ "error": str(e),
65
+ }
benchmarks/ale_bench/ale-bench-lite-problems/ahc025/initial_program.cpp ADDED
@@ -0,0 +1,628 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include <string>
5
+ #include <numeric>
6
+ #include <algorithm>
7
+ #include <iomanip>
8
+ #include <cmath>
9
+ #include <set>
10
+ #include <map>
11
+ #include <chrono>
12
+ #include <random>
13
+
14
+ // Timer
15
+ std::chrono::steady_clock::time_point program_start_time;
16
+ std::chrono::milliseconds time_limit_ms(1850);
17
+
18
+ // Global problem parameters and query counter/cache
19
+ int N_items_global, D_groups_global, Q_total_global;
20
+ int queries_made = 0;
21
+
22
+ std::map<std::pair<int, int>, char> comparison_results_cache_1v1;
23
+ std::map<int, std::map<std::pair<int, int>, char>> comparison_results_cache_1v2_specific;
24
+
25
+ std::mt19937 rng_engine;
26
+
27
+ // Function to perform a query via standard I/O
28
+ char perform_query_actual(const std::vector<int>& L_items, const std::vector<int>& R_items) {
29
+ queries_made++;
30
+ // Debug: #c assignments_array[0] ... assignments_array[N-1]
31
+ // std::cout << "# Query " << queries_made << std::endl;
32
+ std::cout << L_items.size() << " " << R_items.size();
33
+ for (int item_idx : L_items) {
34
+ std::cout << " " << item_idx;
35
+ }
36
+ for (int item_idx : R_items) {
37
+ std::cout << " " << item_idx;
38
+ }
39
+ std::cout << std::endl;
40
+
41
+ char result_char;
42
+ std::cin >> result_char;
43
+ return result_char;
44
+ }
45
+
46
+ char compare_single_items(int item_idx1, int item_idx2) {
47
+ if (item_idx1 == item_idx2) return '=';
48
+
49
+ std::pair<int, int> query_pair_key = {std::min(item_idx1, item_idx2), std::max(item_idx1, item_idx2)};
50
+
51
+ auto it = comparison_results_cache_1v1.find(query_pair_key);
52
+ if (it != comparison_results_cache_1v1.end()) {
53
+ char cached_res = it->second;
54
+ if (item_idx1 == query_pair_key.first) return cached_res;
55
+ return (cached_res == '<' ? '>' : (cached_res == '>' ? '<' : '='));
56
+ }
57
+
58
+ if (queries_made >= Q_total_global) {
59
+ return '=';
60
+ }
61
+
62
+ char res_direct = perform_query_actual({item_idx1}, {item_idx2});
63
+
64
+ if (item_idx1 < item_idx2) {
65
+ comparison_results_cache_1v1[query_pair_key] = res_direct;
66
+ } else {
67
+ char reversed_res = (res_direct == '<' ? '>' : (res_direct == '>' ? '<' : '='));
68
+ comparison_results_cache_1v1[query_pair_key] = reversed_res;
69
+ }
70
+ return res_direct;
71
+ }
72
+
73
+ char compare_1v2_items_specific(int item_curr, int item_prev, int item_s_aux) {
74
+ // Assuming item_curr, item_prev, item_s_aux are distinct indices as per problem context
75
+ // L = {item_curr}, R = {item_prev, item_s_aux}
76
+ // L and R must be disjoint, already true. Each set non-empty.
77
+ // Items within R must be distinct (item_prev != item_s_aux). This is handled by caller logic in X_j estimation.
78
+
79
+ std::pair<int, int> R_pair_key = {std::min(item_prev, item_s_aux), std::max(item_prev, item_s_aux)};
80
+
81
+ auto it_LHS = comparison_results_cache_1v2_specific.find(item_curr);
82
+ if (it_LHS != comparison_results_cache_1v2_specific.end()) {
83
+ auto it_RHS = it_LHS->second.find(R_pair_key);
84
+ if (it_RHS != it_LHS->second.end()) {
85
+ return it_RHS->second;
86
+ }
87
+ }
88
+
89
+ if (queries_made >= Q_total_global) {
90
+ return '=';
91
+ }
92
+
93
+ char res_direct = perform_query_actual({item_curr}, {item_prev, item_s_aux});
94
+ comparison_results_cache_1v2_specific[item_curr][R_pair_key] = res_direct;
95
+ return res_direct;
96
+ }
97
+
98
+ void merge_for_sort(std::vector<int>& items_to_sort, int left, int mid, int right) {
99
+ int n1 = mid - left + 1;
100
+ int n2 = right - mid;
101
+ std::vector<int> L_half(n1), R_half(n2);
102
+ for (int i = 0; i < n1; i++) L_half[i] = items_to_sort[left + i];
103
+ for (int j = 0; j < n2; j++) R_half[j] = items_to_sort[mid + 1 + j];
104
+
105
+ int i = 0, j = 0, k = left;
106
+ while (i < n1 && j < n2) {
107
+ char cmp_res = compare_single_items(L_half[i], R_half[j]);
108
+ if (cmp_res == '<' || cmp_res == '=') {
109
+ items_to_sort[k++] = L_half[i++];
110
+ } else {
111
+ items_to_sort[k++] = R_half[j++];
112
+ }
113
+ }
114
+ while (i < n1) items_to_sort[k++] = L_half[i++];
115
+ while (j < n2) items_to_sort[k++] = R_half[j++];
116
+ }
117
+
118
+ void merge_sort_items(std::vector<int>& items_to_sort, int left, int right) {
119
+ if (left < right) {
120
+ int mid = left + (right - left) / 2;
121
+ merge_sort_items(items_to_sort, left, mid);
122
+ merge_sort_items(items_to_sort, mid + 1, right);
123
+ merge_for_sort(items_to_sort, left, mid, right);
124
+ }
125
+ }
126
+
127
+ long long BASE_WEIGHT = 100000;
128
+
129
+ double estimate_log2(double val) {
130
+ if (val <= 1.0) return 0.0;
131
+ return std::log2(val);
132
+ }
133
+
134
+ int calculate_estimated_query_cost(int N_val, int k_pivots_val) {
135
+ if (k_pivots_val <= 0) return 0;
136
+ if (k_pivots_val == 1) {
137
+ return (N_val > 1) ? (N_val - 1) : 0;
138
+ }
139
+
140
+ double cost = 0;
141
+ cost += static_cast<double>(k_pivots_val) * estimate_log2(static_cast<double>(k_pivots_val));
142
+ for (int j = 2; j < k_pivots_val; ++j) {
143
+ if (j-1 > 0) cost += estimate_log2(static_cast<double>(j - 1));
144
+ }
145
+ cost += static_cast<double>(N_val - k_pivots_val) * estimate_log2(static_cast<double>(k_pivots_val));
146
+ return static_cast<int>(std::ceil(cost));
147
+ }
148
+
149
+ double calculate_variance_from_sums(double sum_sq_group_totals, double total_weight_double, int D_val) {
150
+ if (D_val <= 0) return 1e18;
151
+ double mean_weight = total_weight_double / D_val;
152
+ double variance = sum_sq_group_totals / D_val - mean_weight * mean_weight;
153
+ return std::max(0.0, variance);
154
+ }
155
+
156
+
157
+ int main() {
158
+ std::ios_base::sync_with_stdio(false);
159
+ std::cin.tie(NULL);
160
+
161
+ program_start_time = std::chrono::steady_clock::now();
162
+ uint64_t random_seed = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
163
+ rng_engine.seed(random_seed);
164
+
165
+ std::cin >> N_items_global >> D_groups_global >> Q_total_global;
166
+
167
+ std::vector<long long> estimated_weights(N_items_global);
168
+
169
+ int k_pivots_chosen = (N_items_global > 0) ? 1 : 0;
170
+ if (N_items_global > 1) {
171
+ for (int cur_k_val = N_items_global; cur_k_val >= 1; --cur_k_val) {
172
+ if (calculate_estimated_query_cost(N_items_global, cur_k_val) <= Q_total_global) {
173
+ k_pivots_chosen = cur_k_val;
174
+ break;
175
+ }
176
+ }
177
+ }
178
+ k_pivots_chosen = std::min(k_pivots_chosen, N_items_global);
179
+ if (N_items_global == 0) k_pivots_chosen = 0;
180
+
181
+
182
+ std::vector<int> pivot_item_indices(k_pivots_chosen);
183
+ if (k_pivots_chosen > 0) {
184
+ std::vector<int> all_item_indices_temp(N_items_global);
185
+ std::iota(all_item_indices_temp.begin(), all_item_indices_temp.end(), 0);
186
+ std::shuffle(all_item_indices_temp.begin(), all_item_indices_temp.end(), rng_engine);
187
+ for (int i = 0; i < k_pivots_chosen; ++i) pivot_item_indices[i] = all_item_indices_temp[i];
188
+ }
189
+
190
+ std::vector<int> sorted_pivot_item_indices = pivot_item_indices;
191
+
192
+ // Factors from previous attempt (more aggressive & symmetric):
193
+ const int FACTOR_GT_NUM = 200;
194
+ const int FACTOR_LT_NUM = 50;
195
+ const int FACTOR_XJ_FALLBACK_NUM = 100;
196
+
197
+ if (k_pivots_chosen == 0) {
198
+ for (int i = 0; i < N_items_global; ++i) estimated_weights[i] = BASE_WEIGHT;
199
+ } else if (k_pivots_chosen == 1) {
200
+ estimated_weights[pivot_item_indices[0]] = BASE_WEIGHT;
201
+ for (int i = 0; i < N_items_global; ++i) {
202
+ if (i == pivot_item_indices[0]) continue;
203
+ char res = compare_single_items(i, pivot_item_indices[0]);
204
+ if (res == '=') estimated_weights[i] = BASE_WEIGHT;
205
+ else if (res == '<') estimated_weights[i] = std::max(1LL, BASE_WEIGHT * FACTOR_LT_NUM / 100);
206
+ else estimated_weights[i] = std::max(1LL, BASE_WEIGHT * FACTOR_GT_NUM / 100);
207
+ }
208
+ } else { // k_pivots_chosen >= 2
209
+ merge_sort_items(sorted_pivot_item_indices, 0, k_pivots_chosen - 1);
210
+
211
+ int p0_idx = sorted_pivot_item_indices[0];
212
+ estimated_weights[p0_idx] = BASE_WEIGHT;
213
+
214
+ int p1_idx = sorted_pivot_item_indices[1];
215
+ char res_p1_vs_p0 = compare_single_items(p1_idx, p0_idx);
216
+
217
+ if (res_p1_vs_p0 == '=') {
218
+ estimated_weights[p1_idx] = estimated_weights[p0_idx];
219
+ } else if (res_p1_vs_p0 == '<') {
220
+ estimated_weights[p1_idx] = std::max(1LL, estimated_weights[p0_idx] * FACTOR_LT_NUM / 100);
221
+ } else {
222
+ estimated_weights[p1_idx] = std::max(1LL, estimated_weights[p0_idx] * FACTOR_GT_NUM / 100);
223
+ }
224
+ // Ensure monotonicity and strictness if comparison was strict
225
+ if (estimated_weights[p1_idx] < estimated_weights[p0_idx]) {
226
+ estimated_weights[p1_idx] = estimated_weights[p0_idx];
227
+ }
228
+ if (res_p1_vs_p0 == '>' && estimated_weights[p1_idx] == estimated_weights[p0_idx]) {
229
+ estimated_weights[p1_idx] = estimated_weights[p0_idx] + 1;
230
+ }
231
+
232
+ const long long MAX_XJ_INITIAL_HIGH_BOUND = BASE_WEIGHT * (1LL * N_items_global / std::max(1, D_groups_global) + 10); // Increased +5 to +10 for safety margin
233
+
234
+ for (int j = 2; j < k_pivots_chosen; ++j) {
235
+ int current_pivot_idx = sorted_pivot_item_indices[j];
236
+ int prev_pivot_idx = sorted_pivot_item_indices[j-1];
237
+
238
+ char res_curr_vs_prev = compare_single_items(current_pivot_idx, prev_pivot_idx);
239
+ if (res_curr_vs_prev == '=') {
240
+ estimated_weights[current_pivot_idx] = estimated_weights[prev_pivot_idx];
241
+ } else if (res_curr_vs_prev == '<') {
242
+ estimated_weights[current_pivot_idx] = std::max(1LL, estimated_weights[prev_pivot_idx] * FACTOR_LT_NUM / 100);
243
+ } else {
244
+ long long X_low_bound_val = 1;
245
+ long long X_high_bound_val = MAX_XJ_INITIAL_HIGH_BOUND;
246
+ bool x_low_modified = false;
247
+ bool x_high_modified = false;
248
+
249
+ int s_search_low_arr_idx = 0, s_search_high_arr_idx = j - 2;
250
+
251
+ int num_s_candidates = (s_search_high_arr_idx - s_search_low_arr_idx + 1);
252
+ int queries_for_this_Xj = 0;
253
+ if (num_s_candidates > 0) {
254
+ queries_for_this_Xj = static_cast<int>(std::ceil(estimate_log2(static_cast<double>(num_s_candidates))));
255
+ if (num_s_candidates == 1) queries_for_this_Xj = 1;
256
+ }
257
+
258
+ for(int bs_iter = 0; bs_iter < queries_for_this_Xj && queries_made < Q_total_global; ++bs_iter) {
259
+ if (s_search_low_arr_idx > s_search_high_arr_idx) break;
260
+ int s_mid_arr_idx = s_search_low_arr_idx + (s_search_high_arr_idx - s_search_low_arr_idx) / 2;
261
+ int item_s_aux_idx = sorted_pivot_item_indices[s_mid_arr_idx];
262
+
263
+ // Skip if s_aux is same as prev_pivot_idx; R items must be distinct for query.
264
+ // This should not happen if s_aux is chosen from p0...p_{j-2} and prev_pivot is p_{j-1}.
265
+ // if (item_s_aux_idx == prev_pivot_idx) continue; // Should not be necessary
266
+
267
+ char res_1v2 = compare_1v2_items_specific(current_pivot_idx, prev_pivot_idx, item_s_aux_idx);
268
+
269
+ if (res_1v2 == '=') {
270
+ X_low_bound_val = X_high_bound_val = estimated_weights[item_s_aux_idx];
271
+ x_low_modified = x_high_modified = true;
272
+ break;
273
+ } else if (res_1v2 == '<') {
274
+ X_high_bound_val = estimated_weights[item_s_aux_idx];
275
+ x_high_modified = true;
276
+ s_search_high_arr_idx = s_mid_arr_idx - 1;
277
+ } else { // res_1v2 == '>'
278
+ X_low_bound_val = estimated_weights[item_s_aux_idx];
279
+ x_low_modified = true;
280
+ s_search_low_arr_idx = s_mid_arr_idx + 1;
281
+ }
282
+ }
283
+
284
+ long long estimated_X_j;
285
+ if (x_low_modified && !x_high_modified) { // X_j > X_low_bound_val (max s_aux smaller than X_j)
286
+ estimated_X_j = X_low_bound_val * FACTOR_GT_NUM / 100;
287
+ } else if (!x_low_modified && x_high_modified) { // X_j < X_high_bound_val (min s_aux larger than X_j)
288
+ estimated_X_j = X_high_bound_val * FACTOR_LT_NUM / 100;
289
+ } else if (x_low_modified && x_high_modified) { // X_j is bracketed
290
+ // Reverted to ARITHMETIC MEAN for X_j
291
+ estimated_X_j = (X_low_bound_val + X_high_bound_val) / 2;
292
+ } else { // Fallback if binary search didn't narrow down X_j
293
+ estimated_X_j = estimated_weights[prev_pivot_idx] * FACTOR_XJ_FALLBACK_NUM / 100;
294
+ if (estimated_weights[prev_pivot_idx] > 0 && estimated_X_j == 0) estimated_X_j = 1;
295
+ else if (estimated_weights[prev_pivot_idx] == 0) {
296
+ estimated_X_j = std::max(1LL, BASE_WEIGHT * FACTOR_XJ_FALLBACK_NUM / 100);
297
+ }
298
+ }
299
+ estimated_X_j = std::max(1LL, estimated_X_j);
300
+
301
+ estimated_weights[current_pivot_idx] = estimated_weights[prev_pivot_idx] + estimated_X_j;
302
+ }
303
+ // Ensure monotonicity and strictness
304
+ if(estimated_weights[current_pivot_idx] < estimated_weights[prev_pivot_idx]) {
305
+ estimated_weights[current_pivot_idx] = estimated_weights[prev_pivot_idx];
306
+ }
307
+ if (res_curr_vs_prev == '>' && estimated_weights[current_pivot_idx] == estimated_weights[prev_pivot_idx]) {
308
+ estimated_weights[current_pivot_idx] = estimated_weights[prev_pivot_idx] + 1;
309
+ }
310
+ }
311
+
312
+ // Estimate weights for non-pivot items
313
+ for (int i=0; i<N_items_global; ++i) {
314
+ bool is_pivot_flag = false;
315
+ for(int p_idx_val=0; p_idx_val<k_pivots_chosen; ++p_idx_val) {
316
+ if(sorted_pivot_item_indices[p_idx_val] == i) {
317
+ is_pivot_flag = true;
318
+ break;
319
+ }
320
+ }
321
+ if (is_pivot_flag) continue;
322
+
323
+ int bs_low_arr_idx = 0, bs_high_arr_idx = k_pivots_chosen - 1;
324
+ int found_pivot_idx_for_eq = -1;
325
+
326
+ while(bs_low_arr_idx <= bs_high_arr_idx) {
327
+ if (queries_made >= Q_total_global && found_pivot_idx_for_eq == -1) break; // Stop if out of queries unless already found exact
328
+ int mid_p_arr_idx = bs_low_arr_idx + (bs_high_arr_idx - bs_low_arr_idx) / 2;
329
+ char res_item_vs_p = compare_single_items(i, sorted_pivot_item_indices[mid_p_arr_idx]);
330
+
331
+ if (res_item_vs_p == '=') {
332
+ found_pivot_idx_for_eq = mid_p_arr_idx;
333
+ break;
334
+ } else if (res_item_vs_p == '<') {
335
+ bs_high_arr_idx = mid_p_arr_idx - 1;
336
+ } else {
337
+ bs_low_arr_idx = mid_p_arr_idx + 1;
338
+ }
339
+ }
340
+
341
+ if (found_pivot_idx_for_eq != -1) {
342
+ estimated_weights[i] = estimated_weights[sorted_pivot_item_indices[found_pivot_idx_for_eq]];
343
+ continue;
344
+ }
345
+
346
+ int insert_pos_arr_idx = bs_low_arr_idx;
347
+
348
+ if (insert_pos_arr_idx == 0) { // Smaller than p0
349
+ long long w_p0 = estimated_weights[sorted_pivot_item_indices[0]];
350
+ if (k_pivots_chosen >= 2) {
351
+ long long w_p1 = estimated_weights[sorted_pivot_item_indices[1]];
352
+ // Ensure w_p1 != 0 before division, and w_p0 must be < w_p1 for this extrapolation to make sense
353
+ if (w_p1 > w_p0 && w_p0 > 0 && w_p1 != 0) { // w_p1 should not be 0 if weights are >=1
354
+ estimated_weights[i] = std::max(1LL, w_p0 * w_p0 / w_p1);
355
+ } else {
356
+ estimated_weights[i] = std::max(1LL, w_p0 * FACTOR_LT_NUM / 100);
357
+ }
358
+ } else { // Only p0 exists
359
+ estimated_weights[i] = std::max(1LL, w_p0 * FACTOR_LT_NUM / 100);
360
+ }
361
+ } else if (insert_pos_arr_idx == k_pivots_chosen) { // Larger than p_{k-1}
362
+ long long w_pk_1 = estimated_weights[sorted_pivot_item_indices[k_pivots_chosen-1]];
363
+ if (k_pivots_chosen >= 2) {
364
+ long long w_pk_2 = estimated_weights[sorted_pivot_item_indices[k_pivots_chosen-2]];
365
+ // Ensure w_pk_2 != 0 and w_pk_2 < w_pk_1
366
+ if (w_pk_1 > w_pk_2 && w_pk_2 > 0 && w_pk_2 != 0) { // w_pk_2 should not be 0
367
+ estimated_weights[i] = std::max(1LL, w_pk_1 * w_pk_1 / w_pk_2);
368
+ } else {
369
+ estimated_weights[i] = std::max(1LL, w_pk_1 * FACTOR_GT_NUM / 100);
370
+ }
371
+ } else { // Only p0 exists (which is p_{k-1} here)
372
+ estimated_weights[i] = std::max(1LL, w_pk_1 * FACTOR_GT_NUM / 100);
373
+ }
374
+ } else { // Between p_{idx-1} and p_{idx}
375
+ long long w_prev_p = estimated_weights[sorted_pivot_item_indices[insert_pos_arr_idx-1]];
376
+ long long w_next_p = estimated_weights[sorted_pivot_item_indices[insert_pos_arr_idx]];
377
+ // Geometric mean for interpolation is generally preferred for exponential-like data
378
+ if (w_prev_p > 0 && w_next_p > 0) {
379
+ estimated_weights[i] = static_cast<long long>(std::sqrt(static_cast<double>(w_prev_p) * w_next_p));
380
+ } else { // Fallback for safety or if one weight is zero (should be >=1)
381
+ estimated_weights[i] = (w_prev_p + w_next_p) / 2;
382
+ }
383
+ // Ensure estimate is within the bounds of the two pivots it's between
384
+ estimated_weights[i] = std::max(w_prev_p, estimated_weights[i]);
385
+ estimated_weights[i] = std::min(w_next_p, estimated_weights[i]);
386
+ }
387
+ if (estimated_weights[i] <=0) estimated_weights[i] = 1;
388
+ }
389
+ }
390
+
391
+ // Final check: all weights must be at least 1.
392
+ for(int i=0; i<N_items_global; ++i) {
393
+ if (estimated_weights[i] <= 0) {
394
+ // This state indicates a flaw in estimation logic or extreme case.
395
+ // Fallback to a reasonable default like BASE_WEIGHT or 1.
396
+ // Previous version used BASE_WEIGHT. Smallest possible is 1.
397
+ // Using 1 might be safer if other weights are also small.
398
+ // However, if most are large, BASE_WEIGHT might be better.
399
+ // Sticking to previous fallback.
400
+ estimated_weights[i] = BASE_WEIGHT;
401
+ }
402
+ }
403
+
404
+ // Exhaust remaining queries
405
+ int dummy_item_0_idx = 0;
406
+ int dummy_item_1_idx = 1;
407
+ // N_items_global >= 30, so 0 and 1 are valid and distinct indices.
408
+ while(queries_made < Q_total_global) {
409
+ perform_query_actual({dummy_item_0_idx}, {dummy_item_1_idx});
410
+ // Cycle one of the items to make queries slightly different, though not critical for correctness
411
+ dummy_item_1_idx = (dummy_item_1_idx + 1) % N_items_global;
412
+ if (dummy_item_1_idx == dummy_item_0_idx) { // Ensure distinctness
413
+ dummy_item_1_idx = (dummy_item_1_idx + 1) % N_items_global;
414
+ }
415
+ }
416
+
417
+ // --- Assignment Phase: Greedy followed by Simulated Annealing ---
418
+ std::vector<int> assignment_array(N_items_global);
419
+ std::vector<long long> group_sums_array(D_groups_global, 0);
420
+ long long total_sum_est_val = 0;
421
+
422
+ std::vector<std::vector<int>> group_items_indices(D_groups_global);
423
+ std::vector<int> item_pos_in_group_vector(N_items_global);
424
+
425
+ std::vector<std::pair<long long, int>> items_sorted_for_greedy(N_items_global);
426
+ for(int i=0; i<N_items_global; ++i) {
427
+ items_sorted_for_greedy[i] = {-estimated_weights[i], i};
428
+ }
429
+ std::sort(items_sorted_for_greedy.begin(), items_sorted_for_greedy.end());
430
+
431
+ for(int i=0; i<N_items_global; ++i) {
432
+ int item_actual_idx = items_sorted_for_greedy[i].second;
433
+ long long item_w = estimated_weights[item_actual_idx];
434
+ int best_grp_current = 0;
435
+ if (D_groups_global > 1) {
436
+ long long min_sum_in_group = group_sums_array[0];
437
+ // Small optimization: if multiple groups have same min_sum, pick one randomly or by index
438
+ // Current logic picks smallest index. This is fine.
439
+ for(int j=1; j<D_groups_global; ++j) {
440
+ if (group_sums_array[j] < min_sum_in_group) {
441
+ min_sum_in_group = group_sums_array[j];
442
+ best_grp_current = j;
443
+ }
444
+ }
445
+ }
446
+ assignment_array[item_actual_idx] = best_grp_current;
447
+ group_sums_array[best_grp_current] += item_w;
448
+ group_items_indices[best_grp_current].push_back(item_actual_idx);
449
+ item_pos_in_group_vector[item_actual_idx] = group_items_indices[best_grp_current].size() - 1;
450
+ total_sum_est_val += item_w;
451
+ }
452
+
453
+ double current_sum_sq_group_totals = 0;
454
+ for(long long s : group_sums_array) {
455
+ current_sum_sq_group_totals += static_cast<double>(s) * s;
456
+ }
457
+ double current_var = calculate_variance_from_sums(current_sum_sq_group_totals, static_cast<double>(total_sum_est_val), D_groups_global);
458
+
459
+ // SA Parameters
460
+ double T_initial_factor = 0.25;
461
+ double T = std::max(1.0, current_var * T_initial_factor);
462
+ if (total_sum_est_val > 0 && current_var < 1e-9 && D_groups_global > 0) {
463
+ T = std::max(1.0, static_cast<double>(total_sum_est_val) / std::max(1,N_items_global) * 0.1);
464
+ } else if (total_sum_est_val == 0 && D_groups_global > 0) {
465
+ T = std::max(1.0, static_cast<double>(BASE_WEIGHT) * N_items_global / D_groups_global * 0.01 );
466
+ }
467
+ if (D_groups_global <= 1) T = 0;
468
+
469
+ double cool_rate = 0.9999;
470
+ int sa_iters_count = 0;
471
+ std::uniform_real_distribution<double> unif_dist(0.0, 1.0);
472
+ int no_improvement_streak = 0;
473
+ const int REHEAT_STREAK_THRESH_FACTOR = N_items_global > 50 ? 10 : 20;
474
+ const int CHECK_TIME_INTERVAL = 256;
475
+
476
+
477
+ while(D_groups_global > 1 && N_items_global > 0) {
478
+ sa_iters_count++;
479
+ if (sa_iters_count % CHECK_TIME_INTERVAL == 0) {
480
+ auto time_now = std::chrono::steady_clock::now();
481
+ if (std::chrono::duration_cast<std::chrono::milliseconds>(time_now - program_start_time) >= time_limit_ms) {
482
+ break;
483
+ }
484
+ T *= cool_rate;
485
+ if (no_improvement_streak > N_items_global * REHEAT_STREAK_THRESH_FACTOR && T < current_var * 0.05 && current_var > 1.0 + 1e-9) {
486
+ T = std::max(1.0, current_var * T_initial_factor * 0.5);
487
+ no_improvement_streak = 0;
488
+ }
489
+ }
490
+ if (T < 1e-12 && current_var > 1e-9) T = 1e-9; // Floor T if var high but T too low
491
+ if (T < 1e-12 && current_var < (1.0 + 1e-9)) break; // Converged or T too low
492
+
493
+
494
+ int move_type_rand_val = rng_engine();
495
+ // Adjust probability of swap vs relocate: 1/3 swap, 2/3 relocate
496
+ bool try_swap_move = ( (move_type_rand_val % 3 == 0) );
497
+
498
+ if (!try_swap_move) { // Relocate an item
499
+ if (N_items_global == 0) continue;
500
+ int item_to_move_idx = rng_engine() % N_items_global;
501
+ int old_grp_idx = assignment_array[item_to_move_idx];
502
+
503
+ if (D_groups_global <=1) continue;
504
+ int new_grp_idx = rng_engine() % D_groups_global;
505
+ while(new_grp_idx == old_grp_idx) new_grp_idx = rng_engine() % D_groups_global;
506
+
507
+ long long item_w_val = estimated_weights[item_to_move_idx];
508
+
509
+ long long old_sum_grp_A = group_sums_array[old_grp_idx];
510
+ long long old_sum_grp_B = group_sums_array[new_grp_idx];
511
+ long long new_sum_grp_A = old_sum_grp_A - item_w_val;
512
+ long long new_sum_grp_B = old_sum_grp_B + item_w_val;
513
+
514
+ double new_sum_sq_group_totals_cand = current_sum_sq_group_totals;
515
+ new_sum_sq_group_totals_cand -= static_cast<double>(old_sum_grp_A)*old_sum_grp_A + static_cast<double>(old_sum_grp_B)*old_sum_grp_B;
516
+ new_sum_sq_group_totals_cand += static_cast<double>(new_sum_grp_A)*new_sum_grp_A + static_cast<double>(new_sum_grp_B)*new_sum_grp_B;
517
+ double new_var = calculate_variance_from_sums(new_sum_sq_group_totals_cand, static_cast<double>(total_sum_est_val), D_groups_global);
518
+
519
+ double delta_V = new_var - current_var;
520
+
521
+ if (delta_V < 0 || (T > 1e-12 && unif_dist(rng_engine) < std::exp(-delta_V / T)) ) {
522
+ current_var = new_var;
523
+ current_sum_sq_group_totals = new_sum_sq_group_totals_cand;
524
+ group_sums_array[old_grp_idx] = new_sum_grp_A;
525
+ group_sums_array[new_grp_idx] = new_sum_grp_B;
526
+ assignment_array[item_to_move_idx] = new_grp_idx;
527
+
528
+ int pos_in_old_vec = item_pos_in_group_vector[item_to_move_idx];
529
+ if (!group_items_indices[old_grp_idx].empty()) {
530
+ int last_item_in_old_grp_vec = group_items_indices[old_grp_idx].back();
531
+ if (item_to_move_idx != last_item_in_old_grp_vec) {
532
+ group_items_indices[old_grp_idx][pos_in_old_vec] = last_item_in_old_grp_vec;
533
+ item_pos_in_group_vector[last_item_in_old_grp_vec] = pos_in_old_vec;
534
+ }
535
+ group_items_indices[old_grp_idx].pop_back();
536
+ }
537
+
538
+ group_items_indices[new_grp_idx].push_back(item_to_move_idx);
539
+ item_pos_in_group_vector[item_to_move_idx] = group_items_indices[new_grp_idx].size() - 1;
540
+
541
+ if (delta_V < -1e-9) no_improvement_streak = 0; else no_improvement_streak++;
542
+ } else {
543
+ no_improvement_streak++;
544
+ }
545
+ } else { // Try swap move
546
+ if (D_groups_global <= 1) continue;
547
+
548
+ int grp1_idx = rng_engine() % D_groups_global;
549
+ int grp2_idx = rng_engine() % D_groups_global;
550
+ while(grp2_idx == grp1_idx) grp2_idx = rng_engine() % D_groups_global;
551
+
552
+ if(group_items_indices[grp1_idx].empty() || group_items_indices[grp2_idx].empty()) {
553
+ no_improvement_streak++;
554
+ continue;
555
+ }
556
+
557
+ int item1_original_idx = group_items_indices[grp1_idx][rng_engine() % group_items_indices[grp1_idx].size()];
558
+ int item2_original_idx = group_items_indices[grp2_idx][rng_engine() % group_items_indices[grp2_idx].size()];
559
+
560
+ long long w1 = estimated_weights[item1_original_idx];
561
+ long long w2 = estimated_weights[item2_original_idx];
562
+
563
+ // If w1 == w2, swap has no effect on sums, so delta_V = 0.
564
+ // This move is only useful if it helps escape local minimum for other reasons,
565
+ // or if it's accepted by chance and enables further moves.
566
+ // If w1 == w2, delta_V will be 0. Acceptance depends on T (always if T>0).
567
+ // No need to explicitly check for w1==w2.
568
+
569
+ long long old_sum_grp1 = group_sums_array[grp1_idx];
570
+ long long old_sum_grp2 = group_sums_array[grp2_idx];
571
+ long long new_sum_grp1 = old_sum_grp1 - w1 + w2;
572
+ long long new_sum_grp2 = old_sum_grp2 - w2 + w1;
573
+
574
+ double new_sum_sq_group_totals_cand = current_sum_sq_group_totals;
575
+ new_sum_sq_group_totals_cand -= static_cast<double>(old_sum_grp1)*old_sum_grp1 + static_cast<double>(old_sum_grp2)*old_sum_grp2;
576
+ new_sum_sq_group_totals_cand += static_cast<double>(new_sum_grp1)*new_sum_grp1 + static_cast<double>(new_sum_grp2)*new_sum_grp2;
577
+ double new_var = calculate_variance_from_sums(new_sum_sq_group_totals_cand, static_cast<double>(total_sum_est_val), D_groups_global);
578
+
579
+ double delta_V = new_var - current_var;
580
+
581
+ if (delta_V < 0 || (T > 1e-12 && unif_dist(rng_engine) < std::exp(-delta_V / T)) ) {
582
+ current_var = new_var;
583
+ current_sum_sq_group_totals = new_sum_sq_group_totals_cand;
584
+ group_sums_array[grp1_idx] = new_sum_grp1;
585
+ group_sums_array[grp2_idx] = new_sum_grp2;
586
+
587
+ assignment_array[item1_original_idx] = grp2_idx;
588
+ assignment_array[item2_original_idx] = grp1_idx;
589
+
590
+ // Update item tracking structures
591
+ int pos1_in_G1 = item_pos_in_group_vector[item1_original_idx];
592
+ // group_items_indices[grp1_idx] cannot be empty here as item1 was picked from it.
593
+ int back1_of_G1 = group_items_indices[grp1_idx].back();
594
+ if (item1_original_idx != back1_of_G1) {
595
+ group_items_indices[grp1_idx][pos1_in_G1] = back1_of_G1;
596
+ item_pos_in_group_vector[back1_of_G1] = pos1_in_G1;
597
+ }
598
+ group_items_indices[grp1_idx].pop_back();
599
+
600
+ int pos2_in_G2 = item_pos_in_group_vector[item2_original_idx];
601
+ int back2_of_G2 = group_items_indices[grp2_idx].back();
602
+ if (item2_original_idx != back2_of_G2) {
603
+ group_items_indices[grp2_idx][pos2_in_G2] = back2_of_G2;
604
+ item_pos_in_group_vector[back2_of_G2] = pos2_in_G2;
605
+ }
606
+ group_items_indices[grp2_idx].pop_back();
607
+
608
+ group_items_indices[grp2_idx].push_back(item1_original_idx);
609
+ item_pos_in_group_vector[item1_original_idx] = group_items_indices[grp2_idx].size() - 1;
610
+
611
+ group_items_indices[grp1_idx].push_back(item2_original_idx);
612
+ item_pos_in_group_vector[item2_original_idx] = group_items_indices[grp1_idx].size() - 1;
613
+
614
+ if (delta_V < -1e-9) no_improvement_streak = 0; else no_improvement_streak++;
615
+ } else {
616
+ no_improvement_streak++;
617
+ }
618
+ }
619
+ }
620
+
621
+ for (int i = 0; i < N_items_global; ++i) {
622
+ std::cout << assignment_array[i] << (i == N_items_global - 1 ? "" : " ");
623
+ }
624
+ std::cout << std::endl;
625
+
626
+ return 0;
627
+ }
628
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc026/best_program.cpp ADDED
@@ -0,0 +1,653 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include <string>
5
+ #include <algorithm>
6
+ #include <limits>
7
+ #include <chrono>
8
+ #include <random>
9
+ #include <cmath>
10
+ // #include <queue> // Not strictly needed now for beam search pruning strategy
11
+ #include <utility> // For std::pair, std::move
12
+ #include <span> // For std::span (C++20)
13
+
14
+ // Using 0-indexed internally for stacks and box values for convenience with vectors
15
+ // Box values 0 to N-1, stack indices 0 to M-1.
16
+ // Input is 1-indexed for box values (1 to N) and stack indices (1 to M).
17
+ // Output must be 1-indexed for box values and stack indices.
18
+ // target_stack_idx=0 for carry-out operation.
19
+
20
+ // Constants for heuristic evaluation
21
+ const double HEURISTIC_EMPTY_STACK_BONUS_SCORE = 1000.0;
22
+ const double STACK_HEIGHT_PENALTY_FACTOR = 0.1;
23
+ const int HEURISTIC_LOOKAHEAD_WINDOW = 8;
24
+ const double HEURISTIC_COVER_CRITICAL_PENALTY_PER_BOX_ABOVE = 3.0;
25
+ const double HEURISTIC_MIN_LABEL_IN_DEST_FACTOR = 0.06;
26
+ // Extra heuristic terms (penalize burying small labels)
27
+ const double SMALL_TOP_PENALTY = 45.0;
28
+ const double DEST_SMALL_COUNT_PENALTY = 1.7;
29
+ const double BLOCK_MIN_OVER_SMALL_DEST_PENALTY = 20.0;
30
+ // Beam tie-breaker weights (lower is better): prefer states with fewer covered near-future small labels,
31
+ // slightly penalize total height, and mildly prefer having empty stacks.
32
+ const double BEAM_TIE_COVERED_SMALL_WEIGHT = 0.6;
33
+ const double BEAM_TIE_HEIGHT_WEIGHT = 0.02;
34
+ const double BEAM_TIE_EMPTY_STACK_BONUS = 1.0;
35
+
36
+ // SA Parameters
37
+ const double TIME_LIMIT_SECONDS_TOTAL = 1.95;
38
+ double BEAM_SEARCH_TIME_LIMIT_SECONDS_PARAM = 0.30;
39
+ const int BEAM_WIDTH = 5;
40
+ double T_INITIAL_SA = 75.0;
41
+ double T_FINAL_SA = 0.01;
42
+
43
+ // --- Random Number Generation ---
44
+ struct RandomGenerator {
45
+ std::mt19937 rng;
46
+ RandomGenerator() : rng(std::chrono::steady_clock::now().time_since_epoch().count()) {}
47
+
48
+ int an_int(int min_val, int max_val) {
49
+ if (min_val > max_val) return min_val;
50
+ std::uniform_int_distribution<int> dist(min_val, max_val);
51
+ return dist(rng);
52
+ }
53
+
54
+ double a_double(double min_val, double max_val) {
55
+ std::uniform_real_distribution<double> dist(min_val, max_val);
56
+ return dist(rng);
57
+ }
58
+ } RGen;
59
+
60
+ auto GLOBAL_START_TIME = std::chrono::steady_clock::now();
61
+
62
+ // --- State Definition ---
63
+ struct State {
64
+ std::vector<std::vector<int>> stacks;
65
+ std::vector<std::pair<int, int>> box_pos; // {stack_idx, height_idx}
66
+ long long energy_cost;
67
+ std::vector<std::pair<int, int>> ops_history;
68
+ int N_val;
69
+ int M_val;
70
+ bool record_ops_flag;
71
+
72
+ State() : energy_cost(0), N_val(0), M_val(0), record_ops_flag(true) {}
73
+
74
+ State(int N_in, int M_in, const std::vector<std::vector<int>>& initial_stacks_input, bool rec_ops = true)
75
+ : energy_cost(0), N_val(N_in), M_val(M_in), record_ops_flag(rec_ops) {
76
+ stacks.resize(M_val);
77
+ for (int i = 0; i < M_val; ++i) {
78
+ stacks[i].reserve(N_val + 20);
79
+ }
80
+ box_pos.resize(N_val);
81
+ if (record_ops_flag) {
82
+ ops_history.reserve(N_val * 2 + 50);
83
+ }
84
+
85
+ for (int i = 0; i < M_val; ++i) {
86
+ for (size_t j = 0; j < initial_stacks_input[i].size(); ++j) {
87
+ int box_id = initial_stacks_input[i][j] - 1; // 0-indexed
88
+ stacks[i].push_back(box_id);
89
+ box_pos[box_id] = {i, (int)j};
90
+ }
91
+ }
92
+ }
93
+
94
+ State(const State& other) = default;
95
+ State& operator=(const State& other) = default;
96
+ State(State&& other) noexcept = default;
97
+ State& operator=(State&& other) noexcept = default;
98
+
99
+ double evaluate_destination_stack_choice(
100
+ int current_target_box_val, // 0-indexed
101
+ std::span<const int> block_to_move, // 0-indexed box values
102
+ int dest_stack_idx) const { // 0-indexed
103
+ /**
104
+ Heuristic evaluation for choosing destination stack for the obstructing block.
105
+ Lower score is better. Terms:
106
+ - strong preference to use empty stacks
107
+ - mild penalty by destination height
108
+ - prefer stacks whose minimum label is large
109
+ - penalize covering near-future small labels in destination and inside moved block
110
+ - avoid burying very small labels (dest top in small window or dest min < block min)
111
+ */
112
+ const auto& dest_stack_content = stacks[dest_stack_idx];
113
+ double current_score = 0;
114
+
115
+ const int small_thr = current_target_box_val + HEURISTIC_LOOKAHEAD_WINDOW;
116
+
117
+ if (dest_stack_content.empty()) {
118
+ current_score -= HEURISTIC_EMPTY_STACK_BONUS_SCORE;
119
+ } else {
120
+ current_score += (double)dest_stack_content.size() * STACK_HEIGHT_PENALTY_FACTOR;
121
+
122
+ int min_label_in_dest_stack = N_val;
123
+ int dest_small_count = 0;
124
+ for (int box_val_in_dest : dest_stack_content) {
125
+ min_label_in_dest_stack = std::min(min_label_in_dest_stack, box_val_in_dest);
126
+ if (box_val_in_dest > current_target_box_val && box_val_in_dest <= small_thr) {
127
+ ++dest_small_count;
128
+ }
129
+ }
130
+ current_score -= (double)min_label_in_dest_stack * HEURISTIC_MIN_LABEL_IN_DEST_FACTOR;
131
+
132
+ // Penalize if the destination top is a small value (will be buried)
133
+ int dest_top = dest_stack_content.back();
134
+ if (dest_top <= small_thr) {
135
+ current_score += SMALL_TOP_PENALTY * (int)block_to_move.size();
136
+ }
137
+ // Penalize by how many near-future small labels exist in destination
138
+ current_score += DEST_SMALL_COUNT_PENALTY * dest_small_count * (int)block_to_move.size();
139
+
140
+ // If the destination contains very small labels (min <= block_min), avoid burying them
141
+ if (!block_to_move.empty()) {
142
+ int block_min = N_val;
143
+ for (int v : block_to_move) block_min = std::min(block_min, v);
144
+ if (min_label_in_dest_stack <= block_min) {
145
+ current_score += BLOCK_MIN_OVER_SMALL_DEST_PENALTY * (int)block_to_move.size();
146
+ }
147
+ }
148
+ }
149
+
150
+ // Penalize for near-future small labels inside the moved block that would get buried within the block
151
+ for (size_t i = 0; i < block_to_move.size(); ++i) {
152
+ int box_in_block = block_to_move[i];
153
+ if (box_in_block > current_target_box_val && box_in_block <= small_thr) {
154
+ int boxes_on_top_in_block = (int)block_to_move.size() - 1 - (int)i;
155
+ current_score += HEURISTIC_COVER_CRITICAL_PENALTY_PER_BOX_ABOVE * boxes_on_top_in_block;
156
+ }
157
+ }
158
+ return current_score;
159
+ }
160
+
161
+ void apply_op1_move(int first_box_in_block_val, int num_moved_boxes, int dest_stack_idx) { // All 0-indexed
162
+ int src_stack_idx = box_pos[first_box_in_block_val].first;
163
+ int first_box_height_idx_in_src = box_pos[first_box_in_block_val].second;
164
+
165
+ auto& src_stack_vec = stacks[src_stack_idx];
166
+ auto& dest_stack_vec = stacks[dest_stack_idx];
167
+
168
+ auto P_k_start_iter = src_stack_vec.begin() + first_box_height_idx_in_src;
169
+ auto P_k_end_iter = src_stack_vec.begin() + first_box_height_idx_in_src + num_moved_boxes;
170
+
171
+ int old_dest_stack_height = dest_stack_vec.size();
172
+ dest_stack_vec.insert(dest_stack_vec.end(),
173
+ std::make_move_iterator(P_k_start_iter),
174
+ std::make_move_iterator(P_k_end_iter));
175
+
176
+ for (int i = 0; i < num_moved_boxes; ++i) {
177
+ int moved_box_val = dest_stack_vec[old_dest_stack_height + i];
178
+ box_pos[moved_box_val] = {dest_stack_idx, old_dest_stack_height + i};
179
+ }
180
+
181
+ src_stack_vec.erase(P_k_start_iter, P_k_end_iter);
182
+
183
+ energy_cost += (num_moved_boxes + 1);
184
+ if (record_ops_flag) {
185
+ ops_history.push_back({first_box_in_block_val + 1, dest_stack_idx + 1});
186
+ }
187
+ }
188
+
189
+ void apply_op2_carry_out(int target_box_val) { // 0-indexed
190
+ int stack_idx = box_pos[target_box_val].first;
191
+ stacks[stack_idx].pop_back();
192
+ if (record_ops_flag) {
193
+ ops_history.push_back({target_box_val + 1, 0});
194
+ }
195
+ }
196
+ };
197
+
198
+ struct BeamNode {
199
+ State current_board_state;
200
+ std::vector<int> partial_plan_D;
201
+ double tie_score;
202
+
203
+ BeamNode() : tie_score(0.0) {}
204
+ BeamNode(State state, std::vector<int> plan)
205
+ : current_board_state(std::move(state)), partial_plan_D(std::move(plan)), tie_score(0.0) {}
206
+
207
+ // Order by energy first; if tied, use a lightweight heuristic tie-breaker computed per node.
208
+ bool operator<(const BeamNode& other) const {
209
+ if (current_board_state.energy_cost != other.current_board_state.energy_cost)
210
+ return current_board_state.energy_cost < other.current_board_state.energy_cost;
211
+ return tie_score < other.tie_score;
212
+ }
213
+ };
214
+
215
+ // Compute a tie-breaker score for beam nodes (lower is better).
216
+ // Approach: look ahead to the next target (k = plan length). Sum how many boxes cover
217
+ // near-future small labels (k < v <= k + HEURISTIC_LOOKAHEAD_WINDOW), weighted by their depth.
218
+ // Add a mild total-height penalty, and subtract a small bonus per empty stack.
219
+ static inline double compute_beam_tie_score(const State& S, int next_target_k) {
220
+ const int small_thr = std::min(S.N_val - 1, next_target_k + HEURISTIC_LOOKAHEAD_WINDOW);
221
+ long long covered_sum = 0;
222
+ long long total_height = 0;
223
+ int empty_count = 0;
224
+ for (const auto& st : S.stacks) {
225
+ const int h = (int)st.size();
226
+ total_height += h;
227
+ if (h == 0) { ++empty_count; continue; }
228
+ for (int idx = 0; idx < h; ++idx) {
229
+ int v = st[idx];
230
+ if (v > next_target_k && v <= small_thr) {
231
+ covered_sum += (h - 1 - idx); // boxes above this near-future small label
232
+ }
233
+ }
234
+ }
235
+ return covered_sum * BEAM_TIE_COVERED_SMALL_WEIGHT
236
+ + total_height * BEAM_TIE_HEIGHT_WEIGHT
237
+ - empty_count * BEAM_TIE_EMPTY_STACK_BONUS;
238
+ }
239
+
240
+ std::vector<int> generate_initial_plan_beam_search(
241
+ const std::vector<std::vector<int>>& initial_stacks_param,
242
+ int N_CONST, int M_CONST, int beam_width_param, double max_duration_for_beam_search) {
243
+ /**
244
+ Build an initial destination plan by beam search over k=0..N-1.
245
+ Each step carries if already on top or moves the obstructing block once to each destination,
246
+ keeping the best few states (by energy so far). If time runs out, greedily complete the head node.
247
+ */
248
+ std::vector<BeamNode> beam;
249
+ beam.reserve(beam_width_param);
250
+
251
+ State initial_state_for_beam(N_CONST, M_CONST, initial_stacks_param, false);
252
+ beam.emplace_back(std::move(initial_state_for_beam), std::vector<int>());
253
+ if (N_CONST > 0) beam.back().partial_plan_D.reserve(N_CONST);
254
+ // Initialize tie-score for the root node (next target k = 0)
255
+ beam.back().tie_score = compute_beam_tie_score(beam.back().current_board_state, 0);
256
+
257
+ std::vector<BeamNode> candidates;
258
+ if (M_CONST > 0) candidates.reserve(beam_width_param * M_CONST + 5);
259
+ else candidates.reserve(beam_width_param + 5);
260
+
261
+
262
+ for (int k_target_box = 0; k_target_box < N_CONST; ++k_target_box) {
263
+ double elapsed_seconds_so_far = std::chrono::duration<double>(std::chrono::steady_clock::now() - GLOBAL_START_TIME).count();
264
+ bool time_is_up = elapsed_seconds_so_far > max_duration_for_beam_search;
265
+
266
+ if (time_is_up) {
267
+ if (beam.empty()) {
268
+ std::vector<int> emergency_plan(N_CONST);
269
+ if (N_CONST == 0) return emergency_plan;
270
+ for (int i = 0; i < N_CONST; ++i) emergency_plan[i] = RGen.an_int(0, M_CONST - 1);
271
+ return emergency_plan;
272
+ }
273
+ std::sort(beam.begin(), beam.end());
274
+ BeamNode& best_node_so_far = beam[0];
275
+
276
+ for (int k_future = k_target_box; k_future < N_CONST; ++k_future) {
277
+ State& S_greedy = best_node_so_far.current_board_state;
278
+ int f_target_val = k_future;
279
+ int f_src_idx = S_greedy.box_pos[f_target_val].first;
280
+ int f_h_idx = S_greedy.box_pos[f_target_val].second;
281
+ int f_num_top = S_greedy.stacks[f_src_idx].size() - 1 - f_h_idx;
282
+
283
+ if (f_num_top == 0) {
284
+ best_node_so_far.partial_plan_D.push_back(f_src_idx);
285
+ } else {
286
+ int f_block_first_val = S_greedy.stacks[f_src_idx][f_h_idx + 1];
287
+
288
+ std::span<const int> block_span_greedy;
289
+ if (f_num_top > 0) {
290
+ block_span_greedy = std::span<const int>(S_greedy.stacks[f_src_idx].data() + f_h_idx + 1, f_num_top);
291
+ }
292
+
293
+ double min_h_eval_score = std::numeric_limits<double>::max();
294
+ int best_d_greedy = (M_CONST > 1) ? (f_src_idx + 1) % M_CONST : 0;
295
+
296
+ for (int d_cand = 0; d_cand < M_CONST; ++d_cand) {
297
+ if (d_cand == f_src_idx) continue;
298
+ double h_eval_score = S_greedy.evaluate_destination_stack_choice(k_future, block_span_greedy, d_cand);
299
+ if (h_eval_score < min_h_eval_score) {
300
+ min_h_eval_score = h_eval_score;
301
+ best_d_greedy = d_cand;
302
+ }
303
+ }
304
+ best_node_so_far.partial_plan_D.push_back(best_d_greedy);
305
+ S_greedy.apply_op1_move(f_block_first_val, f_num_top, best_d_greedy);
306
+ }
307
+ S_greedy.apply_op2_carry_out(f_target_val);
308
+ }
309
+ return best_node_so_far.partial_plan_D;
310
+ }
311
+
312
+ candidates.clear();
313
+ for (auto& current_beam_node : beam) {
314
+ State& S_curr = current_beam_node.current_board_state;
315
+ int target_val = k_target_box;
316
+ int src_idx = S_curr.box_pos[target_val].first;
317
+ int h_idx = S_curr.box_pos[target_val].second;
318
+ int num_top = S_curr.stacks[src_idx].size() - 1 - h_idx;
319
+
320
+ if (num_top == 0) {
321
+ State next_S = S_curr;
322
+ std::vector<int> next_plan = current_beam_node.partial_plan_D;
323
+ next_plan.push_back(src_idx);
324
+ next_S.apply_op2_carry_out(target_val);
325
+ candidates.emplace_back(std::move(next_S), std::move(next_plan));
326
+ } else {
327
+ int block_first_val = S_curr.stacks[src_idx][h_idx + 1];
328
+
329
+ std::span<const int> block_span_bs;
330
+ if (num_top > 0) {
331
+ block_span_bs = std::span<const int>(S_curr.stacks[src_idx].data() + h_idx + 1, num_top);
332
+ }
333
+
334
+ for (int dest_cand = 0; dest_cand < M_CONST; ++dest_cand) {
335
+ if (dest_cand == src_idx) continue;
336
+ State next_S = S_curr;
337
+ std::vector<int> next_plan = current_beam_node.partial_plan_D;
338
+ next_plan.push_back(dest_cand);
339
+ next_S.apply_op1_move(block_first_val, num_top, dest_cand);
340
+ next_S.apply_op2_carry_out(target_val);
341
+ BeamNode cand_node(std::move(next_S), std::move(next_plan));
342
+ cand_node.tie_score = compute_beam_tie_score(cand_node.current_board_state, (int)cand_node.partial_plan_D.size());
343
+ candidates.emplace_back(std::move(cand_node));
344
+ }
345
+ }
346
+ }
347
+
348
+ if (candidates.empty()) {
349
+ std::vector<int> emergency_plan(N_CONST);
350
+ if (N_CONST == 0) return emergency_plan;
351
+ if (beam.empty() && N_CONST > 0) {
352
+ for (int i = 0; i < N_CONST; ++i) emergency_plan[i] = RGen.an_int(0, M_CONST - 1);
353
+ return emergency_plan;
354
+ }
355
+ // If candidates is empty but beam was not (e.g. M=1 case for Op1 where no valid dest_cand)
356
+ // For M=10, this shouldn't happen unless beam_width is too small or other issues.
357
+ // Fallback to random plan completion from best current beam node.
358
+ // This is tricky, for now, if candidates is empty, signal failure for this path.
359
+ // The outer logic will pick best from beam if one exists or ultimately generate full random.
360
+ // If `beam` was non-empty but all paths led to no candidates, return best plan so far or random.
361
+ // The current fallback is just random full plan.
362
+ for (int i = 0; i < N_CONST; ++i) emergency_plan[i] = RGen.an_int(0, M_CONST - 1);
363
+ return emergency_plan;
364
+ }
365
+
366
+ std::sort(candidates.begin(), candidates.end());
367
+
368
+ beam.clear();
369
+ for (size_t i = 0; i < std::min((size_t)beam_width_param, candidates.size()); ++i) {
370
+ beam.push_back(std::move(candidates[i]));
371
+ }
372
+
373
+ if (beam.empty() && N_CONST > 0) {
374
+ std::vector<int> emergency_plan(N_CONST);
375
+ for (int i = 0; i < N_CONST; ++i) emergency_plan[i] = RGen.an_int(0, M_CONST - 1);
376
+ return emergency_plan;
377
+ }
378
+ }
379
+
380
+ if (beam.empty()){
381
+ std::vector<int> emergency_plan(N_CONST);
382
+ if (N_CONST == 0) return emergency_plan;
383
+ for (int i = 0; i < N_CONST; ++i) emergency_plan[i] = RGen.an_int(0, M_CONST - 1);
384
+ return emergency_plan;
385
+ }
386
+ std::sort(beam.begin(), beam.end());
387
+ return beam[0].partial_plan_D;
388
+ }
389
+
390
+ struct SimulationResult {
391
+ long long energy_cost;
392
+ std::vector<std::pair<int, int>> ops_history;
393
+ };
394
+
395
+ std::pair<State, long long> simulate_up_to_k(
396
+ const std::vector<std::vector<int>>& initial_stacks_param,
397
+ const std::vector<int>& plan_D,
398
+ int N_CONST, int M_CONST,
399
+ int k_limit_box_idx) {
400
+
401
+ State current_sim_state(N_CONST, M_CONST, initial_stacks_param, false);
402
+
403
+ for (int k_target_box = 0; k_target_box < k_limit_box_idx; ++k_target_box) {
404
+ int target_box_val = k_target_box;
405
+ int src_stack_idx = current_sim_state.box_pos[target_box_val].first;
406
+ int height_idx = current_sim_state.box_pos[target_box_val].second;
407
+ int num_boxes_on_top = current_sim_state.stacks[src_stack_idx].size() - 1 - height_idx;
408
+
409
+ if (num_boxes_on_top > 0) {
410
+ int op1_first_box_in_block_val = current_sim_state.stacks[src_stack_idx][height_idx + 1];
411
+ int actual_dest_stack_idx = plan_D[k_target_box];
412
+
413
+ if (actual_dest_stack_idx == src_stack_idx && M_CONST > 1) {
414
+ actual_dest_stack_idx = (src_stack_idx + 1) % M_CONST;
415
+ }
416
+
417
+ if (M_CONST > 1) {
418
+ current_sim_state.apply_op1_move(op1_first_box_in_block_val, num_boxes_on_top, actual_dest_stack_idx);
419
+ }
420
+ }
421
+ current_sim_state.apply_op2_carry_out(target_box_val);
422
+ }
423
+ long long final_energy = current_sim_state.energy_cost; // Store before move
424
+ return {std::move(current_sim_state), final_energy};
425
+ }
426
+
427
+ SimulationResult run_simulation_from_intermediate_state(
428
+ State intermediate_state,
429
+ const std::vector<int>& plan_D,
430
+ int k_start_box_idx,
431
+ int N_CONST, int M_CONST,
432
+ bool record_ops_for_suffix) {
433
+
434
+ State current_sim_state = std::move(intermediate_state);
435
+
436
+ if (record_ops_for_suffix) {
437
+ current_sim_state.ops_history.clear();
438
+ current_sim_state.record_ops_flag = true;
439
+ } else {
440
+ current_sim_state.record_ops_flag = false;
441
+ }
442
+
443
+ for (int k_target_box = k_start_box_idx; k_target_box < N_CONST; ++k_target_box) {
444
+ int target_box_val = k_target_box;
445
+ int src_stack_idx = current_sim_state.box_pos[target_box_val].first;
446
+ int height_idx = current_sim_state.box_pos[target_box_val].second;
447
+ int num_boxes_on_top = current_sim_state.stacks[src_stack_idx].size() - 1 - height_idx;
448
+
449
+ if (num_boxes_on_top > 0) {
450
+ int op1_first_box_in_block_val = current_sim_state.stacks[src_stack_idx][height_idx + 1];
451
+ int actual_dest_stack_idx = plan_D[k_target_box];
452
+
453
+ if (actual_dest_stack_idx == src_stack_idx && M_CONST > 1) {
454
+ actual_dest_stack_idx = (src_stack_idx + 1) % M_CONST;
455
+ }
456
+
457
+ if (M_CONST > 1) {
458
+ current_sim_state.apply_op1_move(op1_first_box_in_block_val, num_boxes_on_top, actual_dest_stack_idx);
459
+ }
460
+ }
461
+ current_sim_state.apply_op2_carry_out(target_box_val);
462
+ }
463
+ return {current_sim_state.energy_cost, std::move(current_sim_state.ops_history)};
464
+ }
465
+
466
+ SimulationResult run_simulation(const std::vector<std::vector<int>>& initial_stacks_param,
467
+ const std::vector<int>& plan_D, int N_CONST, int M_CONST, bool record_all_ops = true) {
468
+ /**
469
+ Convenience wrapper to simulate from the initial configuration.
470
+ */
471
+ State initial_state_for_full_sim(N_CONST, M_CONST, initial_stacks_param, record_all_ops);
472
+ return run_simulation_from_intermediate_state(std::move(initial_state_for_full_sim), plan_D, 0, N_CONST, M_CONST, record_all_ops);
473
+ }
474
+
475
+ int main() {
476
+ /**
477
+ Driver:
478
+ - read instance
479
+ - beam search for seed plan
480
+ - simulate and refine by SA with partial re-evaluation
481
+ - output operations
482
+ */
483
+ std::ios_base::sync_with_stdio(false);
484
+ std::cin.tie(NULL);
485
+ GLOBAL_START_TIME = std::chrono::steady_clock::now();
486
+
487
+ int N_CONST, M_CONST;
488
+ std::cin >> N_CONST >> M_CONST;
489
+ std::vector<std::vector<int>> initial_stacks_main(M_CONST, std::vector<int>(N_CONST / M_CONST));
490
+ for (int i = 0; i < M_CONST; ++i) {
491
+ for (int j = 0; j < N_CONST / M_CONST; ++j) {
492
+ std::cin >> initial_stacks_main[i][j];
493
+ }
494
+ }
495
+
496
+ double beam_search_duration_budget = TIME_LIMIT_SECONDS_TOTAL * BEAM_SEARCH_TIME_LIMIT_SECONDS_PARAM;
497
+ std::vector<int> current_plan_D = generate_initial_plan_beam_search(
498
+ initial_stacks_main,
499
+ N_CONST,
500
+ M_CONST,
501
+ BEAM_WIDTH,
502
+ beam_search_duration_budget
503
+ );
504
+
505
+ if (current_plan_D.size() < (size_t)N_CONST && N_CONST > 0) {
506
+ current_plan_D.resize(N_CONST, 0);
507
+ }
508
+ if (N_CONST == 0) {
509
+ current_plan_D.clear();
510
+ }
511
+
512
+ SimulationResult current_sim_res_eval = run_simulation(initial_stacks_main, current_plan_D, N_CONST, M_CONST, false);
513
+ long long current_energy = current_sim_res_eval.energy_cost;
514
+
515
+ std::vector<int> best_plan_D = current_plan_D;
516
+ long long best_energy = current_energy;
517
+
518
+ const int MAX_BLOCK_CHANGE_LEN = (N_CONST == 0) ? 0 : std::max(1, N_CONST / 15);
519
+ double time_for_sa_start_offset = std::chrono::duration<double>(std::chrono::steady_clock::now() - GLOBAL_START_TIME).count();
520
+
521
+ while (true) {
522
+ double elapsed_seconds_total = std::chrono::duration<double>(std::chrono::steady_clock::now() - GLOBAL_START_TIME).count();
523
+ if (elapsed_seconds_total >= TIME_LIMIT_SECONDS_TOTAL - 0.02) break;
524
+
525
+ double elapsed_seconds_sa_phase = elapsed_seconds_total - time_for_sa_start_offset;
526
+ double total_time_for_sa_phase = (TIME_LIMIT_SECONDS_TOTAL - 0.02) - time_for_sa_start_offset;
527
+ if (total_time_for_sa_phase <= 0.001) break;
528
+
529
+ double progress_ratio = std::min(1.0, std::max(0.0, elapsed_seconds_sa_phase / total_time_for_sa_phase));
530
+ double current_temp = T_INITIAL_SA * std::pow(T_FINAL_SA / T_INITIAL_SA, progress_ratio);
531
+ current_temp = std::max(current_temp, T_FINAL_SA);
532
+
533
+ std::vector<int> new_plan_D = current_plan_D;
534
+ long long new_energy;
535
+
536
+ int k_change_start_idx = N_CONST;
537
+
538
+ State state_at_change_point;
539
+ bool can_use_partial_simulation = false;
540
+
541
+ double op_choice_rand = RGen.a_double(0.0, 1.0);
542
+
543
+ if (op_choice_rand < 0.30 && N_CONST > 0) {
544
+ // Single-point random reassignment
545
+ int idx_to_change = RGen.an_int(0, N_CONST - 1);
546
+ k_change_start_idx = idx_to_change;
547
+ new_plan_D[idx_to_change] = RGen.an_int(0, M_CONST - 1);
548
+ auto sim_pair = simulate_up_to_k(initial_stacks_main, new_plan_D, N_CONST, M_CONST, k_change_start_idx);
549
+ state_at_change_point = std::move(sim_pair.first);
550
+ can_use_partial_simulation = true;
551
+
552
+ } else if (op_choice_rand < 0.70 && N_CONST > 0) {
553
+ // Random block reassignment
554
+ int block_op_start_idx_rand = RGen.an_int(0, N_CONST - 1);
555
+ int len = RGen.an_int(1, MAX_BLOCK_CHANGE_LEN);
556
+ k_change_start_idx = N_CONST;
557
+ for(int i=0; i<len; ++i) {
558
+ int current_k_in_plan_rand = (block_op_start_idx_rand + i) % N_CONST;
559
+ k_change_start_idx = std::min(k_change_start_idx, current_k_in_plan_rand);
560
+ new_plan_D[current_k_in_plan_rand] = RGen.an_int(0, M_CONST - 1);
561
+ }
562
+ auto sim_pair = simulate_up_to_k(initial_stacks_main, new_plan_D, N_CONST, M_CONST, k_change_start_idx);
563
+ state_at_change_point = std::move(sim_pair.first);
564
+ can_use_partial_simulation = true;
565
+
566
+ } else if (op_choice_rand < 0.85 && N_CONST > 1) {
567
+ // Swap two indices in the plan to diversify neighborhood
568
+ int a = RGen.an_int(0, N_CONST - 1);
569
+ int b = RGen.an_int(0, N_CONST - 1);
570
+ if (a == b) b = (b + 1) % N_CONST;
571
+ if (a > b) std::swap(a, b);
572
+ k_change_start_idx = a;
573
+ std::swap(new_plan_D[a], new_plan_D[b]);
574
+ auto sim_pair = simulate_up_to_k(initial_stacks_main, new_plan_D, N_CONST, M_CONST, k_change_start_idx);
575
+ state_at_change_point = std::move(sim_pair.first);
576
+ can_use_partial_simulation = true;
577
+
578
+ } else if (N_CONST > 0) {
579
+ // Greedy recompute of a single decision using the heuristic
580
+ int k_to_recompute = RGen.an_int(0, N_CONST - 1);
581
+ k_change_start_idx = k_to_recompute;
582
+
583
+ auto sim_pair_greedy = simulate_up_to_k(initial_stacks_main, current_plan_D, N_CONST, M_CONST, k_change_start_idx);
584
+ State decision_state = std::move(sim_pair_greedy.first);
585
+
586
+ int target_op_val = k_to_recompute;
587
+ int src_op_idx = decision_state.box_pos[target_op_val].first;
588
+ int height_op_idx = decision_state.box_pos[target_op_val].second;
589
+ int num_top_op = decision_state.stacks[src_op_idx].size() - 1 - height_op_idx;
590
+
591
+ if (num_top_op > 0 && M_CONST > 1) {
592
+ std::span<const int> block_span_sa;
593
+ if (num_top_op > 0) {
594
+ block_span_sa = std::span<const int>(decision_state.stacks[src_op_idx].data() + height_op_idx + 1, num_top_op);
595
+ }
596
+
597
+ double min_h_score = std::numeric_limits<double>::max();
598
+ int best_dest_idx = (M_CONST > 1) ? (src_op_idx + 1) % M_CONST : 0;
599
+
600
+ for (int dest_cand = 0; dest_cand < M_CONST; ++dest_cand) {
601
+ if (dest_cand == src_op_idx) continue;
602
+ double h_score = decision_state.evaluate_destination_stack_choice(target_op_val, block_span_sa, dest_cand);
603
+ if (h_score < min_h_score) {
604
+ min_h_score = h_score;
605
+ best_dest_idx = dest_cand;
606
+ }
607
+ }
608
+ new_plan_D[k_to_recompute] = best_dest_idx;
609
+ } else {
610
+ new_plan_D[k_to_recompute] = src_op_idx;
611
+ }
612
+ state_at_change_point = std::move(decision_state);
613
+ can_use_partial_simulation = true;
614
+ } else {
615
+ k_change_start_idx = 0;
616
+ can_use_partial_simulation = false;
617
+ }
618
+
619
+ if (N_CONST == 0) {
620
+ new_energy = 0;
621
+ } else if (!can_use_partial_simulation || k_change_start_idx == 0) {
622
+ // If k_change_start_idx is 0, state_at_change_point is initial state with 0 energy.
623
+ // Full simulation is equivalent and perhaps cleaner.
624
+ new_energy = run_simulation(initial_stacks_main, new_plan_D, N_CONST, M_CONST, false).energy_cost;
625
+ } else {
626
+ SimulationResult suffix_res = run_simulation_from_intermediate_state(std::move(state_at_change_point), new_plan_D, k_change_start_idx, N_CONST, M_CONST, false);
627
+ new_energy = suffix_res.energy_cost;
628
+ }
629
+
630
+ if (new_energy < current_energy) {
631
+ current_energy = new_energy;
632
+ current_plan_D = new_plan_D;
633
+ if (new_energy < best_energy) {
634
+ best_energy = new_energy;
635
+ best_plan_D = new_plan_D;
636
+ }
637
+ } else {
638
+ double delta_energy = new_energy - current_energy;
639
+ if (current_temp > 1e-9 && RGen.a_double(0.0, 1.0) < std::exp(-delta_energy / current_temp)) {
640
+ current_energy = new_energy;
641
+ current_plan_D = new_plan_D;
642
+ }
643
+ }
644
+ }
645
+
646
+ SimulationResult final_sim_result = run_simulation(initial_stacks_main, best_plan_D, N_CONST, M_CONST, true);
647
+ for (const auto& op : final_sim_result.ops_history) {
648
+ std::cout << op.first << " " << op.second << "\n";
649
+ }
650
+
651
+ return 0;
652
+ }
653
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc026/config.yaml ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ALE-Bench ahc026 — AtCoder Heuristic Contest
2
+ # Usage: skydiscover-run initial_program.cpp evaluator.py -c config.yaml -s <strategy>
3
+ language: cpp
4
+ diff_based_generation: true
5
+ max_iterations: 100
6
+ checkpoint_interval: 10
7
+ max_solution_length: 60000
8
+ llm:
9
+ api_base: https://api.openai.com/v1
10
+ models:
11
+ - name: "gpt-5"
12
+ weight: 1.0
13
+ max_tokens: 32000
14
+ timeout: 600
15
+ prompt:
16
+ system_message: "You are a world-class algorithm engineer, and you are very good at programming. Now, you are participating\
17
+ \ in a programming contest. You are asked to solve a heuristic problem, known as an NP-hard problem.\n\nStory\n--------\n\
18
+ AtCoder has $n$ cardboard boxes in a warehouse, which are divided into $m$ vertical stacks.\nEach box is labeled with\
19
+ \ a unique number from $1,\\cdots,n$, and CEO Takahashi wants to carry them out of the warehouse one by one in ascending\
20
+ \ order of their numbers.\nIn order to carry out a box, he needs to move all the boxes on top of it to another stack.\n\
21
+ As Takahashi is a very strong man, he can lift and move as many boxes in a stack at a time, but he expends energy depending\
22
+ \ on the number of boxes he lifts.\nPlease find a way to carry the boxes out that expends as little energy as possible.\n\
23
+ \nProblem Statement\n--------\nThere are $n$ cardboard boxes, each labeled with a unique number from $1,\\cdots,n$, divided\
24
+ \ into $m$ stacks.\nWe refer to the box labeled with the number $v(1\\leq v\\leq n)$ as \"box $v$\" and the $i(1\\leq\
25
+ \ i\\leq m)$-th stack from the left as \"stack $i$\".\nThe number of stacks $m$ is a divisor of $n$, and each stack $i$\
26
+ \ contains $n/m$ boxes, with numbers $b_{i,1},b_{i,2},\\cdots,b_{i,n/m}$ from bottom to top.\n\nYou can repeat the following\
27
+ \ two types of operations up to $5000$ times.\n\n1. Choose one box $v (1\\leq v\\leq n)$ that has not yet been carried\
28
+ \ out. Remove box $v$ and all boxes above it from the current stack and move them to the top of another stack $i(1\\leq\
29
+ \ i\\leq m)$ in the same order. Assume that in the stack $i'$ to which box $v$ belongs, the boxes are numbered $b_{i',1},\
30
+ \ \\cdots, b_{i',h'}$ from bottom to top, with $b_{i',j} = v$. Also, assume that the boxes in the destination stack $i$\
31
+ \ are numbered $b_{i,1}, \\cdots, b_{i,h}$ from bottom to top. After this operation, stack $i'$ will become $b_{i',1},\
32
+ \ \\cdots, b_{i',j-1}$, and stack $i$ will become $b_{i,1}, \\cdots, b_{i,h}, b_{i',j}, \\cdots, b_{i',h'}$. Let the number\
33
+ \ of boxes moved by this operation be $k = h' - j + 1$. Then, $k+1$ units of energy will be expended. If $i=i'$, this\
34
+ \ operation changes nothing and just wastes energy.\n2. If the smallest number among all the remaining boxes is $v$, and\
35
+ \ box $v$ is at the top of a stack, then box $v$ can be carried out. This operation does not expend energy.\n\nOperation\
36
+ \ 1 cannot create a new stack $i>m$, but it can move boxes into an empty stack $i(1\\leq i\\leq m)$ after all boxes have\
37
+ \ been carried out from it by operation 2.\n\nPlease find a sequence of operations that carries out all the boxes with\
38
+ \ as little total energy expenditure as possible.\n\nScoring\n--------\nIf all the boxes are carried out with less or\
39
+ \ equal to $5000$ operations, and the total energy expenditure is $V$, you will obtain a score of $\\max(1, 10000-V)$.\n\
40
+ If you failed to carry out all the boxes, or if you output an illegal operation sequence (specifying out-of-range values\
41
+ \ or a box that has already been carried out, or specifying a box that does not satisfy the condition in operation 2),\
42
+ \ it is judged as <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Wrong Answer\"\
43
+ >WA</span>.\n\nThere are $150$ test cases, and the score of a submission is the total score for each test case.\nIf your\
44
+ \ submission produces an illegal output or exceeds the time limit for some test cases, the submission itself will be judged\
45
+ \ as <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Wrong Answer\">WA</span> or\
46
+ \ <span class='label label-warning' data-toggle='tooltip' data-placement='top' title=\"Time Limit Exceeded\">TLE</span>\
47
+ \ , and the score of the submission will be zero.\nThe highest score obtained during the contest will determine the final\
48
+ \ ranking, and there will be no system test after the contest.\nIf more than one participant gets the same score, they\
49
+ \ will be ranked in the same place regardless of the submission time.\n\n\n\nInput\n--------\nInput is given from Standard\
50
+ \ Input in the following format:\n~~~\n$n$ $m$\n$b_{1,1}$ $\\cdots$ $b_{1,n/m}$\n$\\vdots$\n$b_{m,1}$ $\\cdots$ $b_{m,n/m}$\n\
51
+ ~~~\n\nThe number of boxes $n$ and the number of stacks $m$ are fixed at $n=200$ and <font color='red'>$m=10$</font> in\
52
+ \ all the test cases.\nThe number $b_{i,j}$ represents the number of the $j$-th box from the bottom of stack $i$, and\
53
+ \ satisfies $1\\leq b_{i,j}\\leq n$.\n\nOutput\n--------\nLet the $k$-th operation be represented by the two integers\
54
+ \ $(v_k,i_k)$ as follows.\n\n1. If you use operation 1 to move box $v(1\\leq v\\leq n)$ and all the boxes stacked on it\
55
+ \ to another stack $i(1\\leq i\\leq m)$, then $(v_k,i_k)=(v,i)$.\n2. If you use operation 2 to carry out box $v$, $(v_k,i_k)=(v,0)$.\n\
56
+ \nOutput the obtained sequence of operations $(v_1,i_1),\\cdots,(v_K,i_K)$ ($K\\leq 5000$) to Standard Output in the following\
57
+ \ format:\n~~~\n$v_1$ $i_1$\n$\\vdots$\n$v_K$ $i_K$\n~~~\n\n<a href=\"https://img.atcoder.jp/ahc026/lPQezTZx.html?lang=en&seed=0&output=sample\"\
58
+ >Show example</a>\n\n\nInput Generation\n--------\nThe box numbers are generated by randomly shuffling the integers from\
59
+ \ $1$ to $n$ and then dividing them into groups of $n/m$ each.\n\nTools (Input generator and visualizer)\n--------\n-\
60
+ \ <a href=\"https://img.atcoder.jp/ahc026/lPQezTZx.html?lang=en\">Web version</a>: This is more powerful than the local\
61
+ \ version providing animations.\n- <a href=\"https://img.atcoder.jp/ahc026/lPQezTZx.zip\">Local version</a>: You need\
62
+ \ a compilation environment of <a href=\"https://www.rust-lang.org/\">Rust language</a>.\n - <a href=\"https://img.atcoder.jp/ahc026/lPQezTZx_windows.zip\"\
63
+ >Pre-compiled binary for Windows</a>: If you are not familiar with the Rust language environment, please use this instead.\n\
64
+ \nPlease be aware that sharing visualization results or discussing solutions/ideas during the contest is prohibited.\n\
65
+ \n\n Problem constraints:\n time_limit=2.0 memory_limit=1073741824\n"
66
+ evaluator:
67
+ timeout: 10000
68
+ cascade_evaluation: false
69
+
benchmarks/ale_bench/ale-bench-lite-problems/ahc026/evaluator.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+ from pathlib import Path
3
+ from ale_bench.result import CaseResult, JudgeResult, Result
4
+ from ale_bench_eval.safe_ale_session import start_ale_bench_session
5
+ import logging
6
+ import sys
7
+ logger = logging.getLogger(__name__ + "_" + "ALE_BENCH_EVALUATOR")
8
+
9
+ def result_feedback(result: Result) -> CaseResult:
10
+ if result.overall_judge_result == JudgeResult.ACCEPTED:
11
+ return result.case_results[0]
12
+ else:
13
+ selected_case_idx = 0
14
+ for idx, case_result in enumerate(result.case_results):
15
+ if case_result.judge_result == result.overall_judge_result:
16
+ selected_case_idx = idx
17
+ break
18
+ return result.case_results[selected_case_idx]
19
+
20
+ def evaluate(program_path):
21
+ problem_id = "ahc026"
22
+ logger.info(f"Evaluating program {program_path} for problem {problem_id} in ale bench evaluator")
23
+ try:
24
+ session = None
25
+ logger.info("Starting ALE-Bench session")
26
+ session = start_ale_bench_session(
27
+ problem_id=problem_id,
28
+ lite_version=True,
29
+ num_workers=13,
30
+ )
31
+ logger.info("ALE-Bench session started")
32
+ if not session:
33
+ raise RuntimeError("Failed to start or restart the session.")
34
+ optim_factor = 1 if session.problem.metadata.score_type == "maximize" else -1
35
+ code = Path(program_path).read_text().replace("# EVOLVE-BLOCK-START", "").replace("# EVOLVE-BLOCK-END", "").strip()
36
+ logger.info("Code extracted")
37
+ num_public_cases = 50
38
+ cases = session.case_gen(list(range(num_public_cases)))
39
+ public_result = session.case_eval(
40
+ cases, code, code_language="cpp20", skip_local_visualization=True
41
+ )
42
+ logger.info("Public evaluation completed")
43
+ extracted_case = result_feedback(public_result)
44
+ logger.info("Result feedback completed")
45
+ logger.info("ALE-Bench session closed")
46
+ combined_score = public_result.overall_absolute_score * optim_factor / num_public_cases
47
+ if public_result.overall_judge_result != JudgeResult.ACCEPTED and optim_factor == -1:
48
+ combined_score = -sys.maxsize - 1
49
+ session.close()
50
+ return {
51
+ "judge_result": public_result.overall_judge_result.value,
52
+ "overall_score": public_result.overall_absolute_score,
53
+ "max_execution_time_sec": max([case_result.execution_time for case_result in public_result.case_results]),
54
+ "max_memory_usage_mib": max([case_result.memory_usage for case_result in public_result.case_results]) // 1024 // 1024,
55
+ "standard_error": extracted_case.error_str,
56
+ "message": extracted_case.message,
57
+ "combined_score": combined_score,
58
+ }
59
+ except Exception as e:
60
+ logger.error(f"Evaluation failed completely: {str(e)}")
61
+ logger.error(traceback.format_exc())
62
+ return {
63
+ "overall_score": 0.0,
64
+ "error": str(e),
65
+ }
benchmarks/ale_bench/ale-bench-lite-problems/ahc026/initial_program.cpp ADDED
@@ -0,0 +1,563 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include <string>
5
+ #include <algorithm>
6
+ #include <limits>
7
+ #include <chrono>
8
+ #include <random>
9
+ #include <cmath>
10
+ // #include <queue> // Not strictly needed now for beam search pruning strategy
11
+ #include <utility> // For std::pair, std::move
12
+ #include <span> // For std::span (C++20)
13
+
14
+ // Using 0-indexed internally for stacks and box values for convenience with vectors
15
+ // Box values 0 to N-1, stack indices 0 to M-1.
16
+ // Input is 1-indexed for box values (1 to N) and stack indices (1 to M).
17
+ // Output must be 1-indexed for box values and stack indices.
18
+ // target_stack_idx=0 for carry-out operation.
19
+
20
+ // Constants for heuristic evaluation
21
+ const double HEURISTIC_EMPTY_STACK_BONUS_SCORE = 1000.0;
22
+ const double STACK_HEIGHT_PENALTY_FACTOR = 0.1;
23
+ const int HEURISTIC_LOOKAHEAD_WINDOW = 5;
24
+ const double HEURISTIC_COVER_CRITICAL_PENALTY_PER_BOX_ABOVE = 3.0;
25
+ const double HEURISTIC_MIN_LABEL_IN_DEST_FACTOR = 0.05;
26
+
27
+ // SA Parameters
28
+ const double TIME_LIMIT_SECONDS_TOTAL = 1.95;
29
+ double BEAM_SEARCH_TIME_LIMIT_SECONDS_PARAM = 0.30;
30
+ const int BEAM_WIDTH = 5;
31
+ double T_INITIAL_SA = 75.0;
32
+ double T_FINAL_SA = 0.01;
33
+
34
+ // --- Random Number Generation ---
35
+ struct RandomGenerator {
36
+ std::mt19937 rng;
37
+ RandomGenerator() : rng(std::chrono::steady_clock::now().time_since_epoch().count()) {}
38
+
39
+ int an_int(int min_val, int max_val) {
40
+ if (min_val > max_val) return min_val;
41
+ std::uniform_int_distribution<int> dist(min_val, max_val);
42
+ return dist(rng);
43
+ }
44
+
45
+ double a_double(double min_val, double max_val) {
46
+ std::uniform_real_distribution<double> dist(min_val, max_val);
47
+ return dist(rng);
48
+ }
49
+ } RGen;
50
+
51
+ auto GLOBAL_START_TIME = std::chrono::steady_clock::now();
52
+
53
+ // --- State Definition ---
54
+ struct State {
55
+ std::vector<std::vector<int>> stacks;
56
+ std::vector<std::pair<int, int>> box_pos; // {stack_idx, height_idx}
57
+ long long energy_cost;
58
+ std::vector<std::pair<int, int>> ops_history;
59
+ int N_val;
60
+ int M_val;
61
+ bool record_ops_flag;
62
+
63
+ State() : energy_cost(0), N_val(0), M_val(0), record_ops_flag(true) {}
64
+
65
+ State(int N_in, int M_in, const std::vector<std::vector<int>>& initial_stacks_input, bool rec_ops = true)
66
+ : energy_cost(0), N_val(N_in), M_val(M_in), record_ops_flag(rec_ops) {
67
+ stacks.resize(M_val);
68
+ for (int i = 0; i < M_val; ++i) {
69
+ stacks[i].reserve(N_val + 20);
70
+ }
71
+ box_pos.resize(N_val);
72
+ if (record_ops_flag) {
73
+ ops_history.reserve(N_val * 2 + 50);
74
+ }
75
+
76
+ for (int i = 0; i < M_val; ++i) {
77
+ for (size_t j = 0; j < initial_stacks_input[i].size(); ++j) {
78
+ int box_id = initial_stacks_input[i][j] - 1; // 0-indexed
79
+ stacks[i].push_back(box_id);
80
+ box_pos[box_id] = {i, (int)j};
81
+ }
82
+ }
83
+ }
84
+
85
+ State(const State& other) = default;
86
+ State& operator=(const State& other) = default;
87
+ State(State&& other) noexcept = default;
88
+ State& operator=(State&& other) noexcept = default;
89
+
90
+ double evaluate_destination_stack_choice(
91
+ int current_target_box_val, // 0-indexed
92
+ std::span<const int> block_to_move, // 0-indexed box values
93
+ int dest_stack_idx) const { // 0-indexed
94
+ const auto& dest_stack_content = stacks[dest_stack_idx];
95
+ double current_score = 0;
96
+
97
+ if (dest_stack_content.empty()) {
98
+ current_score -= HEURISTIC_EMPTY_STACK_BONUS_SCORE;
99
+ } else {
100
+ current_score += (double)dest_stack_content.size() * STACK_HEIGHT_PENALTY_FACTOR;
101
+ int min_label_in_dest_stack = N_val;
102
+ for (int box_val_in_dest : dest_stack_content) {
103
+ min_label_in_dest_stack = std::min(min_label_in_dest_stack, box_val_in_dest);
104
+ }
105
+ current_score -= (double)min_label_in_dest_stack * HEURISTIC_MIN_LABEL_IN_DEST_FACTOR;
106
+ }
107
+
108
+ for (int box_in_dest : dest_stack_content) {
109
+ if (box_in_dest > current_target_box_val && box_in_dest < current_target_box_val + HEURISTIC_LOOKAHEAD_WINDOW + 1) {
110
+ current_score += HEURISTIC_COVER_CRITICAL_PENALTY_PER_BOX_ABOVE * block_to_move.size();
111
+ }
112
+ }
113
+
114
+ for (size_t i = 0; i < block_to_move.size(); ++i) {
115
+ int box_in_block = block_to_move[i];
116
+ if (box_in_block > current_target_box_val && box_in_block < current_target_box_val + HEURISTIC_LOOKAHEAD_WINDOW + 1) {
117
+ int boxes_on_top_in_block = block_to_move.size() - 1 - i;
118
+ current_score += HEURISTIC_COVER_CRITICAL_PENALTY_PER_BOX_ABOVE * boxes_on_top_in_block;
119
+ }
120
+ }
121
+ return current_score;
122
+ }
123
+
124
+ void apply_op1_move(int first_box_in_block_val, int num_moved_boxes, int dest_stack_idx) { // All 0-indexed
125
+ int src_stack_idx = box_pos[first_box_in_block_val].first;
126
+ int first_box_height_idx_in_src = box_pos[first_box_in_block_val].second;
127
+
128
+ auto& src_stack_vec = stacks[src_stack_idx];
129
+ auto& dest_stack_vec = stacks[dest_stack_idx];
130
+
131
+ auto P_k_start_iter = src_stack_vec.begin() + first_box_height_idx_in_src;
132
+ auto P_k_end_iter = src_stack_vec.begin() + first_box_height_idx_in_src + num_moved_boxes;
133
+
134
+ int old_dest_stack_height = dest_stack_vec.size();
135
+ dest_stack_vec.insert(dest_stack_vec.end(),
136
+ std::make_move_iterator(P_k_start_iter),
137
+ std::make_move_iterator(P_k_end_iter));
138
+
139
+ for (int i = 0; i < num_moved_boxes; ++i) {
140
+ int moved_box_val = dest_stack_vec[old_dest_stack_height + i];
141
+ box_pos[moved_box_val] = {dest_stack_idx, old_dest_stack_height + i};
142
+ }
143
+
144
+ src_stack_vec.erase(P_k_start_iter, P_k_end_iter);
145
+
146
+ energy_cost += (num_moved_boxes + 1);
147
+ if (record_ops_flag) {
148
+ ops_history.push_back({first_box_in_block_val + 1, dest_stack_idx + 1});
149
+ }
150
+ }
151
+
152
+ void apply_op2_carry_out(int target_box_val) { // 0-indexed
153
+ int stack_idx = box_pos[target_box_val].first;
154
+ stacks[stack_idx].pop_back();
155
+ if (record_ops_flag) {
156
+ ops_history.push_back({target_box_val + 1, 0});
157
+ }
158
+ }
159
+ };
160
+
161
+ struct BeamNode {
162
+ State current_board_state;
163
+ std::vector<int> partial_plan_D;
164
+
165
+ BeamNode() = default;
166
+ BeamNode(State state, std::vector<int> plan)
167
+ : current_board_state(std::move(state)), partial_plan_D(std::move(plan)) {}
168
+
169
+ bool operator<(const BeamNode& other) const {
170
+ return current_board_state.energy_cost < other.current_board_state.energy_cost;
171
+ }
172
+ };
173
+
174
+ std::vector<int> generate_initial_plan_beam_search(
175
+ const std::vector<std::vector<int>>& initial_stacks_param,
176
+ int N_CONST, int M_CONST, int beam_width_param, double max_duration_for_beam_search) {
177
+
178
+ std::vector<BeamNode> beam;
179
+ beam.reserve(beam_width_param);
180
+
181
+ State initial_state_for_beam(N_CONST, M_CONST, initial_stacks_param, false);
182
+ beam.emplace_back(std::move(initial_state_for_beam), std::vector<int>());
183
+ if (N_CONST > 0) beam.back().partial_plan_D.reserve(N_CONST);
184
+
185
+ std::vector<BeamNode> candidates;
186
+ if (M_CONST > 0) candidates.reserve(beam_width_param * M_CONST + 5);
187
+ else candidates.reserve(beam_width_param + 5);
188
+
189
+
190
+ for (int k_target_box = 0; k_target_box < N_CONST; ++k_target_box) {
191
+ double elapsed_seconds_so_far = std::chrono::duration<double>(std::chrono::steady_clock::now() - GLOBAL_START_TIME).count();
192
+ bool time_is_up = elapsed_seconds_so_far > max_duration_for_beam_search;
193
+
194
+ if (time_is_up) {
195
+ if (beam.empty()) {
196
+ std::vector<int> emergency_plan(N_CONST);
197
+ if (N_CONST == 0) return emergency_plan;
198
+ for (int i = 0; i < N_CONST; ++i) emergency_plan[i] = RGen.an_int(0, M_CONST - 1);
199
+ return emergency_plan;
200
+ }
201
+ std::sort(beam.begin(), beam.end());
202
+ BeamNode& best_node_so_far = beam[0];
203
+
204
+ for (int k_future = k_target_box; k_future < N_CONST; ++k_future) {
205
+ State& S_greedy = best_node_so_far.current_board_state;
206
+ int f_target_val = k_future;
207
+ int f_src_idx = S_greedy.box_pos[f_target_val].first;
208
+ int f_h_idx = S_greedy.box_pos[f_target_val].second;
209
+ int f_num_top = S_greedy.stacks[f_src_idx].size() - 1 - f_h_idx;
210
+
211
+ if (f_num_top == 0) {
212
+ best_node_so_far.partial_plan_D.push_back(f_src_idx);
213
+ } else {
214
+ int f_block_first_val = S_greedy.stacks[f_src_idx][f_h_idx + 1];
215
+
216
+ std::span<const int> block_span_greedy;
217
+ if (f_num_top > 0) {
218
+ block_span_greedy = std::span<const int>(S_greedy.stacks[f_src_idx].data() + f_h_idx + 1, f_num_top);
219
+ }
220
+
221
+ double min_h_eval_score = std::numeric_limits<double>::max();
222
+ int best_d_greedy = (M_CONST > 1) ? (f_src_idx + 1) % M_CONST : 0;
223
+
224
+ for (int d_cand = 0; d_cand < M_CONST; ++d_cand) {
225
+ if (d_cand == f_src_idx) continue;
226
+ double h_eval_score = S_greedy.evaluate_destination_stack_choice(k_future, block_span_greedy, d_cand);
227
+ if (h_eval_score < min_h_eval_score) {
228
+ min_h_eval_score = h_eval_score;
229
+ best_d_greedy = d_cand;
230
+ }
231
+ }
232
+ best_node_so_far.partial_plan_D.push_back(best_d_greedy);
233
+ S_greedy.apply_op1_move(f_block_first_val, f_num_top, best_d_greedy);
234
+ }
235
+ S_greedy.apply_op2_carry_out(f_target_val);
236
+ }
237
+ return best_node_so_far.partial_plan_D;
238
+ }
239
+
240
+ candidates.clear();
241
+ for (auto& current_beam_node : beam) {
242
+ State& S_curr = current_beam_node.current_board_state;
243
+ int target_val = k_target_box;
244
+ int src_idx = S_curr.box_pos[target_val].first;
245
+ int h_idx = S_curr.box_pos[target_val].second;
246
+ int num_top = S_curr.stacks[src_idx].size() - 1 - h_idx;
247
+
248
+ if (num_top == 0) {
249
+ State next_S = S_curr;
250
+ std::vector<int> next_plan = current_beam_node.partial_plan_D;
251
+ next_plan.push_back(src_idx);
252
+ next_S.apply_op2_carry_out(target_val);
253
+ candidates.emplace_back(std::move(next_S), std::move(next_plan));
254
+ } else {
255
+ int block_first_val = S_curr.stacks[src_idx][h_idx + 1];
256
+
257
+ std::span<const int> block_span_bs;
258
+ if (num_top > 0) {
259
+ block_span_bs = std::span<const int>(S_curr.stacks[src_idx].data() + h_idx + 1, num_top);
260
+ }
261
+
262
+ for (int dest_cand = 0; dest_cand < M_CONST; ++dest_cand) {
263
+ if (dest_cand == src_idx) continue;
264
+ State next_S = S_curr;
265
+ std::vector<int> next_plan = current_beam_node.partial_plan_D;
266
+ next_plan.push_back(dest_cand);
267
+ next_S.apply_op1_move(block_first_val, num_top, dest_cand);
268
+ next_S.apply_op2_carry_out(target_val);
269
+ candidates.emplace_back(std::move(next_S), std::move(next_plan));
270
+ }
271
+ }
272
+ }
273
+
274
+ if (candidates.empty()) {
275
+ std::vector<int> emergency_plan(N_CONST);
276
+ if (N_CONST == 0) return emergency_plan;
277
+ if (beam.empty() && N_CONST > 0) {
278
+ for (int i = 0; i < N_CONST; ++i) emergency_plan[i] = RGen.an_int(0, M_CONST - 1);
279
+ return emergency_plan;
280
+ }
281
+ // If candidates is empty but beam was not (e.g. M=1 case for Op1 where no valid dest_cand)
282
+ // For M=10, this shouldn't happen unless beam_width is too small or other issues.
283
+ // Fallback to random plan completion from best current beam node.
284
+ // This is tricky, for now, if candidates is empty, signal failure for this path.
285
+ // The outer logic will pick best from beam if one exists or ultimately generate full random.
286
+ // If `beam` was non-empty but all paths led to no candidates, return best plan so far or random.
287
+ // The current fallback is just random full plan.
288
+ for (int i = 0; i < N_CONST; ++i) emergency_plan[i] = RGen.an_int(0, M_CONST - 1);
289
+ return emergency_plan;
290
+ }
291
+
292
+ std::sort(candidates.begin(), candidates.end());
293
+
294
+ beam.clear();
295
+ for (size_t i = 0; i < std::min((size_t)beam_width_param, candidates.size()); ++i) {
296
+ beam.push_back(std::move(candidates[i]));
297
+ }
298
+
299
+ if (beam.empty() && N_CONST > 0) {
300
+ std::vector<int> emergency_plan(N_CONST);
301
+ for (int i = 0; i < N_CONST; ++i) emergency_plan[i] = RGen.an_int(0, M_CONST - 1);
302
+ return emergency_plan;
303
+ }
304
+ }
305
+
306
+ if (beam.empty()){
307
+ std::vector<int> emergency_plan(N_CONST);
308
+ if (N_CONST == 0) return emergency_plan;
309
+ for (int i = 0; i < N_CONST; ++i) emergency_plan[i] = RGen.an_int(0, M_CONST - 1);
310
+ return emergency_plan;
311
+ }
312
+ std::sort(beam.begin(), beam.end());
313
+ return beam[0].partial_plan_D;
314
+ }
315
+
316
+ struct SimulationResult {
317
+ long long energy_cost;
318
+ std::vector<std::pair<int, int>> ops_history;
319
+ };
320
+
321
+ std::pair<State, long long> simulate_up_to_k(
322
+ const std::vector<std::vector<int>>& initial_stacks_param,
323
+ const std::vector<int>& plan_D,
324
+ int N_CONST, int M_CONST,
325
+ int k_limit_box_idx) {
326
+
327
+ State current_sim_state(N_CONST, M_CONST, initial_stacks_param, false);
328
+
329
+ for (int k_target_box = 0; k_target_box < k_limit_box_idx; ++k_target_box) {
330
+ int target_box_val = k_target_box;
331
+ int src_stack_idx = current_sim_state.box_pos[target_box_val].first;
332
+ int height_idx = current_sim_state.box_pos[target_box_val].second;
333
+ int num_boxes_on_top = current_sim_state.stacks[src_stack_idx].size() - 1 - height_idx;
334
+
335
+ if (num_boxes_on_top > 0) {
336
+ int op1_first_box_in_block_val = current_sim_state.stacks[src_stack_idx][height_idx + 1];
337
+ int actual_dest_stack_idx = plan_D[k_target_box];
338
+
339
+ if (actual_dest_stack_idx == src_stack_idx && M_CONST > 1) {
340
+ actual_dest_stack_idx = (src_stack_idx + 1) % M_CONST;
341
+ }
342
+
343
+ if (M_CONST > 1) {
344
+ current_sim_state.apply_op1_move(op1_first_box_in_block_val, num_boxes_on_top, actual_dest_stack_idx);
345
+ }
346
+ }
347
+ current_sim_state.apply_op2_carry_out(target_box_val);
348
+ }
349
+ long long final_energy = current_sim_state.energy_cost; // Store before move
350
+ return {std::move(current_sim_state), final_energy};
351
+ }
352
+
353
+ SimulationResult run_simulation_from_intermediate_state(
354
+ State intermediate_state,
355
+ const std::vector<int>& plan_D,
356
+ int k_start_box_idx,
357
+ int N_CONST, int M_CONST,
358
+ bool record_ops_for_suffix) {
359
+
360
+ State current_sim_state = std::move(intermediate_state);
361
+
362
+ if (record_ops_for_suffix) {
363
+ current_sim_state.ops_history.clear();
364
+ current_sim_state.record_ops_flag = true;
365
+ } else {
366
+ current_sim_state.record_ops_flag = false;
367
+ }
368
+
369
+ for (int k_target_box = k_start_box_idx; k_target_box < N_CONST; ++k_target_box) {
370
+ int target_box_val = k_target_box;
371
+ int src_stack_idx = current_sim_state.box_pos[target_box_val].first;
372
+ int height_idx = current_sim_state.box_pos[target_box_val].second;
373
+ int num_boxes_on_top = current_sim_state.stacks[src_stack_idx].size() - 1 - height_idx;
374
+
375
+ if (num_boxes_on_top > 0) {
376
+ int op1_first_box_in_block_val = current_sim_state.stacks[src_stack_idx][height_idx + 1];
377
+ int actual_dest_stack_idx = plan_D[k_target_box];
378
+
379
+ if (actual_dest_stack_idx == src_stack_idx && M_CONST > 1) {
380
+ actual_dest_stack_idx = (src_stack_idx + 1) % M_CONST;
381
+ }
382
+
383
+ if (M_CONST > 1) {
384
+ current_sim_state.apply_op1_move(op1_first_box_in_block_val, num_boxes_on_top, actual_dest_stack_idx);
385
+ }
386
+ }
387
+ current_sim_state.apply_op2_carry_out(target_box_val);
388
+ }
389
+ return {current_sim_state.energy_cost, std::move(current_sim_state.ops_history)};
390
+ }
391
+
392
+ SimulationResult run_simulation(const std::vector<std::vector<int>>& initial_stacks_param,
393
+ const std::vector<int>& plan_D, int N_CONST, int M_CONST, bool record_all_ops = true) {
394
+ State initial_state_for_full_sim(N_CONST, M_CONST, initial_stacks_param, record_all_ops);
395
+ return run_simulation_from_intermediate_state(std::move(initial_state_for_full_sim), plan_D, 0, N_CONST, M_CONST, record_all_ops);
396
+ }
397
+
398
+ int main() {
399
+ std::ios_base::sync_with_stdio(false);
400
+ std::cin.tie(NULL);
401
+ GLOBAL_START_TIME = std::chrono::steady_clock::now();
402
+
403
+ int N_CONST, M_CONST;
404
+ std::cin >> N_CONST >> M_CONST;
405
+ std::vector<std::vector<int>> initial_stacks_main(M_CONST, std::vector<int>(N_CONST / M_CONST));
406
+ for (int i = 0; i < M_CONST; ++i) {
407
+ for (int j = 0; j < N_CONST / M_CONST; ++j) {
408
+ std::cin >> initial_stacks_main[i][j];
409
+ }
410
+ }
411
+
412
+ double beam_search_duration_budget = TIME_LIMIT_SECONDS_TOTAL * BEAM_SEARCH_TIME_LIMIT_SECONDS_PARAM;
413
+ std::vector<int> current_plan_D = generate_initial_plan_beam_search(
414
+ initial_stacks_main,
415
+ N_CONST,
416
+ M_CONST,
417
+ BEAM_WIDTH,
418
+ beam_search_duration_budget
419
+ );
420
+
421
+ if (current_plan_D.size() < (size_t)N_CONST && N_CONST > 0) {
422
+ current_plan_D.resize(N_CONST, 0);
423
+ }
424
+ if (N_CONST == 0) {
425
+ current_plan_D.clear();
426
+ }
427
+
428
+ SimulationResult current_sim_res_eval = run_simulation(initial_stacks_main, current_plan_D, N_CONST, M_CONST, false);
429
+ long long current_energy = current_sim_res_eval.energy_cost;
430
+
431
+ std::vector<int> best_plan_D = current_plan_D;
432
+ long long best_energy = current_energy;
433
+
434
+ const int MAX_BLOCK_CHANGE_LEN = (N_CONST == 0) ? 0 : std::max(1, N_CONST / 15);
435
+ double time_for_sa_start_offset = std::chrono::duration<double>(std::chrono::steady_clock::now() - GLOBAL_START_TIME).count();
436
+
437
+ while (true) {
438
+ double elapsed_seconds_total = std::chrono::duration<double>(std::chrono::steady_clock::now() - GLOBAL_START_TIME).count();
439
+ if (elapsed_seconds_total >= TIME_LIMIT_SECONDS_TOTAL - 0.02) break;
440
+
441
+ double elapsed_seconds_sa_phase = elapsed_seconds_total - time_for_sa_start_offset;
442
+ double total_time_for_sa_phase = (TIME_LIMIT_SECONDS_TOTAL - 0.02) - time_for_sa_start_offset;
443
+ if (total_time_for_sa_phase <= 0.001) break;
444
+
445
+ double progress_ratio = std::min(1.0, std::max(0.0, elapsed_seconds_sa_phase / total_time_for_sa_phase));
446
+ double current_temp = T_INITIAL_SA * std::pow(T_FINAL_SA / T_INITIAL_SA, progress_ratio);
447
+ current_temp = std::max(current_temp, T_FINAL_SA);
448
+
449
+ std::vector<int> new_plan_D = current_plan_D;
450
+ long long new_energy;
451
+
452
+ int k_change_start_idx = N_CONST;
453
+
454
+ State state_at_change_point;
455
+ bool can_use_partial_simulation = false;
456
+
457
+ double op_choice_rand = RGen.a_double(0.0, 1.0);
458
+
459
+ if (op_choice_rand < 0.35 && N_CONST > 0) {
460
+ int idx_to_change = RGen.an_int(0, N_CONST - 1);
461
+ k_change_start_idx = idx_to_change;
462
+ new_plan_D[idx_to_change] = RGen.an_int(0, M_CONST - 1); // M_CONST is 10, so M_CONST-1 is 9
463
+
464
+ // For partial sim, state needs to be based on prefix of new_plan_D
465
+ auto sim_pair = simulate_up_to_k(initial_stacks_main, new_plan_D, N_CONST, M_CONST, k_change_start_idx);
466
+ state_at_change_point = std::move(sim_pair.first);
467
+ can_use_partial_simulation = true;
468
+
469
+ } else if (op_choice_rand < 0.80 && N_CONST > 0) {
470
+ int block_op_start_idx_rand = RGen.an_int(0, N_CONST - 1);
471
+ int len = RGen.an_int(1, MAX_BLOCK_CHANGE_LEN);
472
+
473
+ k_change_start_idx = N_CONST;
474
+ for(int i=0; i<len; ++i) {
475
+ int current_k_in_plan_rand = (block_op_start_idx_rand + i) % N_CONST;
476
+ k_change_start_idx = std::min(k_change_start_idx, current_k_in_plan_rand);
477
+ new_plan_D[current_k_in_plan_rand] = RGen.an_int(0, M_CONST - 1);
478
+ }
479
+ // For partial sim, state needs to be based on prefix of new_plan_D
480
+ auto sim_pair = simulate_up_to_k(initial_stacks_main, new_plan_D, N_CONST, M_CONST, k_change_start_idx);
481
+ state_at_change_point = std::move(sim_pair.first);
482
+ can_use_partial_simulation = true;
483
+
484
+ } else if (N_CONST > 0) {
485
+ int k_to_recompute = RGen.an_int(0, N_CONST - 1);
486
+ k_change_start_idx = k_to_recompute;
487
+
488
+ // For greedy, decisions are based on current_plan_D's prefix
489
+ // So, simulate current_plan_D up to k_change_start_idx
490
+ auto sim_pair_greedy = simulate_up_to_k(initial_stacks_main, current_plan_D, N_CONST, M_CONST, k_change_start_idx);
491
+ State decision_state = std::move(sim_pair_greedy.first);
492
+
493
+ int target_op_val = k_to_recompute;
494
+ int src_op_idx = decision_state.box_pos[target_op_val].first;
495
+ int height_op_idx = decision_state.box_pos[target_op_val].second;
496
+ int num_top_op = decision_state.stacks[src_op_idx].size() - 1 - height_op_idx;
497
+
498
+ if (num_top_op > 0 && M_CONST > 1) {
499
+ std::span<const int> block_span_sa;
500
+ if (num_top_op > 0) {
501
+ block_span_sa = std::span<const int>(decision_state.stacks[src_op_idx].data() + height_op_idx + 1, num_top_op);
502
+ }
503
+
504
+ double min_h_score = std::numeric_limits<double>::max();
505
+ int best_dest_idx = (M_CONST > 1) ? (src_op_idx + 1) % M_CONST : 0;
506
+
507
+ for (int dest_cand = 0; dest_cand < M_CONST; ++dest_cand) {
508
+ if (dest_cand == src_op_idx) continue;
509
+ double h_score = decision_state.evaluate_destination_stack_choice(target_op_val, block_span_sa, dest_cand);
510
+ if (h_score < min_h_score) {
511
+ min_h_score = h_score;
512
+ best_dest_idx = dest_cand;
513
+ }
514
+ }
515
+ new_plan_D[k_to_recompute] = best_dest_idx;
516
+ } else {
517
+ new_plan_D[k_to_recompute] = src_op_idx;
518
+ }
519
+ // The state for suffix evaluation should be decision_state,
520
+ // as new_plan_D only differs at or after k_change_start_idx.
521
+ // The prefix energy is decision_state.energy_cost.
522
+ state_at_change_point = std::move(decision_state);
523
+ can_use_partial_simulation = true;
524
+ } else {
525
+ k_change_start_idx = 0;
526
+ can_use_partial_simulation = false;
527
+ }
528
+
529
+ if (N_CONST == 0) {
530
+ new_energy = 0;
531
+ } else if (!can_use_partial_simulation || k_change_start_idx == 0) {
532
+ // If k_change_start_idx is 0, state_at_change_point is initial state with 0 energy.
533
+ // Full simulation is equivalent and perhaps cleaner.
534
+ new_energy = run_simulation(initial_stacks_main, new_plan_D, N_CONST, M_CONST, false).energy_cost;
535
+ } else {
536
+ SimulationResult suffix_res = run_simulation_from_intermediate_state(std::move(state_at_change_point), new_plan_D, k_change_start_idx, N_CONST, M_CONST, false);
537
+ new_energy = suffix_res.energy_cost;
538
+ }
539
+
540
+ if (new_energy < current_energy) {
541
+ current_energy = new_energy;
542
+ current_plan_D = new_plan_D;
543
+ if (new_energy < best_energy) {
544
+ best_energy = new_energy;
545
+ best_plan_D = new_plan_D;
546
+ }
547
+ } else {
548
+ double delta_energy = new_energy - current_energy;
549
+ if (current_temp > 1e-9 && RGen.a_double(0.0, 1.0) < std::exp(-delta_energy / current_temp)) {
550
+ current_energy = new_energy;
551
+ current_plan_D = new_plan_D;
552
+ }
553
+ }
554
+ }
555
+
556
+ SimulationResult final_sim_result = run_simulation(initial_stacks_main, best_plan_D, N_CONST, M_CONST, true);
557
+ for (const auto& op : final_sim_result.ops_history) {
558
+ std::cout << op.first << " " << op.second << "\n";
559
+ }
560
+
561
+ return 0;
562
+ }
563
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc027/best_program.cpp ADDED
@@ -0,0 +1,595 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #pragma GCC optimize("O3,unroll-loops")
3
+
4
+ #include <iostream>
5
+ #include <vector>
6
+ #include <string>
7
+ #include <numeric>
8
+ #include <algorithm>
9
+ #include <chrono>
10
+ #include <random>
11
+ #include <iomanip>
12
+ #include <cmath>
13
+ // #include <map> // Not used
14
+
15
+ // Global game data
16
+ int N_GRID_SIZE;
17
+ std::vector<std::string> H_WALLS_INFO;
18
+ std::vector<std::string> V_WALLS_INFO;
19
+ int D_SUSC[40][40];
20
+
21
+ struct Pos {
22
+ int16_t r, c;
23
+ Pos() : r(0), c(0) {}
24
+ Pos(int16_t r_val, int16_t c_val) : r(r_val), c(c_val) {}
25
+
26
+ bool operator==(const Pos& other) const { return r == other.r && c == other.c; }
27
+ bool operator!=(const Pos& other) const { return !(*this == other); }
28
+ bool operator<(const Pos& other) const {
29
+ if (r != other.r) return r < other.r;
30
+ return c < other.c;
31
+ }
32
+ };
33
+
34
+ constexpr int DR[] = {0, 1, 0, -1}; // R, D, L, U
35
+ constexpr int DC[] = {1, 0, -1, 0};
36
+ constexpr char DIR_CHARS[] = {'R', 'D', 'L', 'U'};
37
+ const int MAX_L_PATH = 100000;
38
+ double MAX_L_PATH_HIGH_THRESHOLD_EFFECTIVE;
39
+ double MIN_L_PATH_LOW_THRESHOLD_EFFECTIVE;
40
+
41
+
42
+ std::mt19937 RND_ENGINE;
43
+
44
+ Pos APSP_PARENT[40][40][40][40];
45
+ int APSP_DIST[40][40][40][40];
46
+
47
+ bool is_valid_pos(int r, int c) {
48
+ return r >= 0 && r < N_GRID_SIZE && c >= 0 && c < N_GRID_SIZE;
49
+ }
50
+
51
+ bool check_wall(Pos p_from, Pos p_to) {
52
+ int dr = p_to.r - p_from.r;
53
+ int dc = p_to.c - p_from.c;
54
+ if (dr == 1) { // Down
55
+ return H_WALLS_INFO[p_from.r][p_from.c] == '1';
56
+ } else if (dr == -1) { // Up
57
+ return H_WALLS_INFO[p_to.r][p_to.c] == '1';
58
+ } else if (dc == 1) { // Right
59
+ return V_WALLS_INFO[p_from.r][p_from.c] == '1';
60
+ } else if (dc == -1) { // Left
61
+ return V_WALLS_INFO[p_from.r][p_to.c] == '1';
62
+ }
63
+ return true;
64
+ }
65
+
66
+ char get_move_char(Pos p_from, Pos p_to) {
67
+ int dr = p_to.r - p_from.r;
68
+ int dc = p_to.c - p_from.c;
69
+ for(int i=0; i<4; ++i) if(DR[i] == dr && DC[i] == dc) return DIR_CHARS[i];
70
+ return ' ';
71
+ }
72
+
73
+ // Unused stub retained for compatibility.
74
+ inline char invert_move(char){ return ' '; }
75
+
76
+ void compute_apsp() {
77
+ /*
78
+ All-pairs unweighted shortest paths on the grid.
79
+ For each source cell, we run BFS and store distance and parent to reconstruct
80
+ shortest paths instantly during local edits.
81
+ */
82
+ for (int sr = 0; sr < N_GRID_SIZE; ++sr) {
83
+ for (int sc = 0; sc < N_GRID_SIZE; ++sc) {
84
+ for (int tr = 0; tr < N_GRID_SIZE; ++tr) for (int tc = 0; tc < N_GRID_SIZE; ++tc) APSP_DIST[sr][sc][tr][tc] = -1;
85
+
86
+ std::vector<Pos> q; q.reserve(N_GRID_SIZE * N_GRID_SIZE);
87
+ q.push_back(Pos{(int16_t)sr, (int16_t)sc});
88
+ APSP_DIST[sr][sc][sr][sc] = 0;
89
+
90
+ int head = 0;
91
+ while(head < static_cast<int>(q.size())){
92
+ Pos curr = q[head++];
93
+ for(int i=0; i<4; ++i){
94
+ Pos next_candidate = Pos{(int16_t)(curr.r + DR[i]), (int16_t)(curr.c + DC[i])};
95
+ if(is_valid_pos(next_candidate.r, next_candidate.c) && !check_wall(curr, next_candidate) && APSP_DIST[sr][sc][next_candidate.r][next_candidate.c] == -1){
96
+ APSP_DIST[sr][sc][next_candidate.r][next_candidate.c] = APSP_DIST[sr][sc][curr.r][curr.c] + 1;
97
+ APSP_PARENT[sr][sc][next_candidate.r][next_candidate.c] = curr;
98
+ q.push_back(next_candidate);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ bool get_apsp_moves(Pos p_from, Pos p_to, std::vector<char>& out_moves) {
107
+ out_moves.clear();
108
+ if (p_from == p_to) return true;
109
+ if (APSP_DIST[p_from.r][p_from.c][p_to.r][p_to.c] == -1) return false;
110
+
111
+ out_moves.reserve(APSP_DIST[p_from.r][p_from.c][p_to.r][p_to.c]);
112
+ Pos curr = p_to;
113
+ while(curr != p_from) {
114
+ Pos prev = APSP_PARENT[p_from.r][p_from.c][curr.r][curr.c];
115
+ out_moves.push_back(get_move_char(prev, curr));
116
+ curr = prev;
117
+ }
118
+ std::reverse(out_moves.begin(), out_moves.end());
119
+ return true;
120
+ }
121
+
122
+ std::vector<std::vector<std::vector<int>>> CELL_VISIT_TIMES_GLOBAL_BUFFER;
123
+
124
+ struct CellDirtInfo {
125
+ long double weighted_dirt_contribution;
126
+ Pos p;
127
+ bool operator<(const CellDirtInfo& other) const {
128
+ return weighted_dirt_contribution > other.weighted_dirt_contribution;
129
+ }
130
+ };
131
+ std::vector<CellDirtInfo> TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER;
132
+
133
+ struct PathData {
134
+ std::vector<char> moves;
135
+ std::vector<Pos> coords;
136
+ bool visited_flags[40][40];
137
+ long double score_val;
138
+ long double total_dirt_sum_numerator;
139
+ long double cell_dirt_term_sum[40][40];
140
+
141
+ PathData() : score_val(1e18L), total_dirt_sum_numerator(0.0L) {
142
+ for(int i=0; i<N_GRID_SIZE; ++i) for(int j=0; j<N_GRID_SIZE; ++j) {
143
+ visited_flags[i][j] = false;
144
+ cell_dirt_term_sum[i][j] = 0.0L;
145
+ }
146
+ }
147
+
148
+ PathData(const PathData& other) = default;
149
+ PathData(PathData&& other) = default;
150
+ PathData& operator=(const PathData& other) = default;
151
+ PathData& operator=(PathData&& other) = default;
152
+ };
153
+
154
+ bool update_coords_and_visited_flags(PathData& pd) {
155
+ /*
156
+ Rebuilds the coordinate list from move characters and marks visited cells.
157
+ Validates boundaries and walls. Returns false on any invalid step.
158
+ */
159
+ pd.coords.assign(1, Pos{0,0});
160
+ if (!pd.moves.empty()) {
161
+ pd.coords.reserve(pd.moves.size() + 1);
162
+ }
163
+
164
+ for (int r = 0; r < N_GRID_SIZE; ++r) for (int c = 0; c < N_GRID_SIZE; ++c) pd.visited_flags[r][c] = false;
165
+ pd.visited_flags[0][0] = true;
166
+
167
+ Pos current_p = Pos{0,0};
168
+ for (char move_char : pd.moves) {
169
+ int dir_idx = -1;
170
+ for (int i = 0; i < 4; ++i) if (DIR_CHARS[i] == move_char) dir_idx = i;
171
+
172
+ if (dir_idx == -1) return false;
173
+
174
+ Pos next_p = Pos{(int16_t)(current_p.r + DR[dir_idx]), (int16_t)(current_p.c + DC[dir_idx])};
175
+ if (!is_valid_pos(next_p.r, next_p.c) || check_wall(current_p, next_p)) return false;
176
+
177
+ current_p = next_p;
178
+ pd.coords.push_back(current_p);
179
+ pd.visited_flags[current_p.r][current_p.c] = true;
180
+ }
181
+ return true;
182
+ }
183
+
184
+ void calculate_score_full(PathData& pd) {
185
+ /*
186
+ Computes average dirtiness exactly for a periodic route.
187
+
188
+ For each cell, let the visit times within one cycle be t1 < ... < tk.
189
+ The contribution is sum over gaps g = t_i - t_{i-1} (with wrap-around using t0 = t_k - L) of g*(g-1)/2.
190
+ We accumulate this in O(L + N^2) time without storing per-cell vectors:
191
+ - Track first_visit and last_visit times per cell while scanning the trajectory once.
192
+ - Add wrap-around gap for visited cells.
193
+ - For never-visited cells (should not happen for valid paths) set L*(L-1)/2.
194
+ */
195
+ if (!update_coords_and_visited_flags(pd)) { pd.score_val = 1e18L; return; }
196
+ if (pd.moves.size() > MAX_L_PATH) { pd.score_val = 1e18L; return; }
197
+ if (!pd.moves.empty()) {
198
+ if (pd.coords.back() != Pos{0,0}) { pd.score_val = 1e18L; return; }
199
+ } else {
200
+ if (N_GRID_SIZE > 1) { pd.score_val = 1e18L; return; }
201
+ }
202
+ for (int r = 0; r < N_GRID_SIZE; ++r) for (int c = 0; c < N_GRID_SIZE; ++c)
203
+ if (!pd.visited_flags[r][c]) { pd.score_val = 1e18L; return; }
204
+
205
+ const int L = (int)pd.moves.size();
206
+ if (L == 0) {
207
+ pd.score_val = (N_GRID_SIZE == 1) ? 0.0L : 1e18L;
208
+ pd.total_dirt_sum_numerator = 0.0L;
209
+ if (N_GRID_SIZE == 1) pd.cell_dirt_term_sum[0][0] = 0.0L;
210
+ return;
211
+ }
212
+
213
+ static int first_visit[40][40], last_visit[40][40];
214
+ for (int r=0;r<N_GRID_SIZE;++r) for (int c=0;c<N_GRID_SIZE;++c) {
215
+ first_visit[r][c] = -1; last_visit[r][c] = -1;
216
+ pd.cell_dirt_term_sum[r][c] = 0.0L;
217
+ }
218
+
219
+ for (int t = 1; t <= L; ++t) {
220
+ const Pos& p = pd.coords[t];
221
+ if (last_visit[p.r][p.c] == -1) {
222
+ first_visit[p.r][p.c] = t;
223
+ last_visit[p.r][p.c] = t;
224
+ } else {
225
+ long long delta = (long long)t - (long long)last_visit[p.r][p.c];
226
+ pd.cell_dirt_term_sum[p.r][p.c] += (long double)delta * (delta - 1) / 2.0L;
227
+ last_visit[p.r][p.c] = t;
228
+ }
229
+ }
230
+
231
+ pd.total_dirt_sum_numerator = 0.0L;
232
+ for (int r=0;r<N_GRID_SIZE;++r) for (int c=0;c<N_GRID_SIZE;++c) {
233
+ if (first_visit[r][c] == -1) {
234
+ pd.cell_dirt_term_sum[r][c] = (long double)L * (L - 1) / 2.0L;
235
+ } else {
236
+ long long delta_wrap = (long long)first_visit[r][c] + (long long)L - (long long)last_visit[r][c];
237
+ pd.cell_dirt_term_sum[r][c] += (long double)delta_wrap * (delta_wrap - 1) / 2.0L;
238
+ }
239
+ pd.total_dirt_sum_numerator += (long double)D_SUSC[r][c] * pd.cell_dirt_term_sum[r][c];
240
+ }
241
+ pd.score_val = pd.total_dirt_sum_numerator / L;
242
+ }
243
+
244
+ bool initial_dfs_visited[40][40];
245
+ void generate_initial_dfs_path(int r, int c, PathData& pd) {
246
+ initial_dfs_visited[r][c] = true;
247
+ for (int dir_idx = 0; dir_idx < 4; ++dir_idx) {
248
+ Pos current_p = Pos{(int16_t)r, (int16_t)c};
249
+ Pos next_p = Pos{(int16_t)(r + DR[dir_idx]), (int16_t)(c + DC[dir_idx])};
250
+ if (is_valid_pos(next_p.r, next_p.c) && !check_wall(current_p, next_p) && !initial_dfs_visited[next_p.r][next_p.c]) {
251
+ pd.moves.push_back(DIR_CHARS[dir_idx]);
252
+ generate_initial_dfs_path(next_p.r, next_p.c, pd);
253
+ pd.moves.push_back(DIR_CHARS[(dir_idx + 2) % 4]);
254
+ }
255
+ }
256
+ }
257
+
258
+ Pos select_target_cell_for_dirt_ops(const PathData& current_pd_obj, bool use_sqrt_N_sampling) {
259
+ TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.clear();
260
+ if (current_pd_obj.score_val > 1e17L) {
261
+ std::uniform_int_distribution<int> r_dist(0, N_GRID_SIZE-1);
262
+ return Pos{(int16_t)r_dist(RND_ENGINE), (int16_t)r_dist(RND_ENGINE)};
263
+ }
264
+
265
+ for(int r=0; r<N_GRID_SIZE; ++r) {
266
+ for(int c=0; c<N_GRID_SIZE; ++c) {
267
+ TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.push_back({(long double)D_SUSC[r][c] * current_pd_obj.cell_dirt_term_sum[r][c], Pos{(int16_t)r,(int16_t)c}});
268
+ }
269
+ }
270
+ std::sort(TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.begin(), TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.end());
271
+
272
+ if (TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.empty()) {
273
+ std::uniform_int_distribution<int> r_dist(0, N_GRID_SIZE-1);
274
+ return Pos{(int16_t)r_dist(RND_ENGINE), (int16_t)r_dist(RND_ENGINE)};
275
+ }
276
+
277
+ int K_select;
278
+ if(use_sqrt_N_sampling){
279
+ K_select = std::min((int)TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.size(), std::max(1, N_GRID_SIZE));
280
+ } else {
281
+ K_select = std::min((int)TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.size(), std::max(10, N_GRID_SIZE * N_GRID_SIZE / 10));
282
+ }
283
+
284
+ if (K_select <= 0) {
285
+ std::uniform_int_distribution<int> r_dist(0, N_GRID_SIZE-1);
286
+ return Pos{(int16_t)r_dist(RND_ENGINE), (int16_t)r_dist(RND_ENGINE)};
287
+ }
288
+
289
+ std::uniform_int_distribution<int> top_k_dist(0, K_select - 1);
290
+ return TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER[top_k_dist(RND_ENGINE)].p;
291
+ }
292
+
293
+ const int OP4_SAMPLE_POINTS = 20;
294
+ const int OP5_MAX_SUBSEGMENT_LEN = 20;
295
+
296
+ std::vector<char> GET_APSP_MOVES_BUFFER1;
297
+ std::vector<char> GET_APSP_MOVES_BUFFER2;
298
+
299
+
300
+ int main(int argc, char *argv[]) {
301
+ std::ios_base::sync_with_stdio(false); std::cin.tie(NULL);
302
+
303
+ double time_limit_seconds = 1.95;
304
+ if (argc > 1) time_limit_seconds = std::stod(argv[1]);
305
+
306
+ auto time_start_prog = std::chrono::high_resolution_clock::now();
307
+ RND_ENGINE.seed(std::chrono::system_clock::now().time_since_epoch().count());
308
+
309
+ std::cin >> N_GRID_SIZE;
310
+ H_WALLS_INFO.resize(N_GRID_SIZE - 1);
311
+ V_WALLS_INFO.resize(N_GRID_SIZE);
312
+
313
+ for (int i = 0; i < N_GRID_SIZE - 1; ++i) std::cin >> H_WALLS_INFO[i];
314
+ for (int i = 0; i < N_GRID_SIZE; ++i) std::cin >> V_WALLS_INFO[i];
315
+ for (int i = 0; i < N_GRID_SIZE; ++i) for (int j = 0; j < N_GRID_SIZE; ++j) std::cin >> D_SUSC[i][j];
316
+
317
+ MAX_L_PATH_HIGH_THRESHOLD_EFFECTIVE = MAX_L_PATH * 0.95;
318
+ MIN_L_PATH_LOW_THRESHOLD_EFFECTIVE = N_GRID_SIZE * N_GRID_SIZE;
319
+
320
+ compute_apsp();
321
+
322
+ TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.reserve(N_GRID_SIZE * N_GRID_SIZE);
323
+ GET_APSP_MOVES_BUFFER1.reserve(N_GRID_SIZE*N_GRID_SIZE*2);
324
+ GET_APSP_MOVES_BUFFER2.reserve(N_GRID_SIZE*N_GRID_SIZE*2);
325
+
326
+
327
+ PathData current_pd_obj;
328
+ for(int i=0; i<N_GRID_SIZE; ++i) for(int j=0; j<N_GRID_SIZE; ++j) initial_dfs_visited[i][j] = false;
329
+ generate_initial_dfs_path(0, 0, current_pd_obj);
330
+ calculate_score_full(current_pd_obj);
331
+
332
+ PathData best_pd_obj = current_pd_obj;
333
+
334
+ double start_temp = 5000.0 * sqrt(N_GRID_SIZE);
335
+ double end_temp = 0.1;
336
+
337
+ int iterations_count = 0;
338
+ PathData candidate_pd_obj;
339
+ std::uniform_real_distribution<double> accept_dist_01(0.0, 1.0);
340
+
341
+
342
+ while(true) {
343
+ iterations_count++;
344
+ if(iterations_count % 100 == 0){
345
+ auto now_time = std::chrono::high_resolution_clock::now();
346
+ double elapsed_seconds = std::chrono::duration<double>(now_time - time_start_prog).count();
347
+ if (elapsed_seconds > time_limit_seconds) break;
348
+ }
349
+
350
+ int L_curr = current_pd_obj.moves.size();
351
+
352
+ bool modified_successfully = false;
353
+ for (int try_op = 0; try_op < 10; ++try_op) {
354
+ candidate_pd_obj = current_pd_obj;
355
+
356
+ std::uniform_int_distribution<int> op_dist(0, 99);
357
+ int op_choice_val = op_dist(RND_ENGINE);
358
+ int operation_type = -1;
359
+
360
+ if (op_choice_val < 15) operation_type = 0;
361
+ else if (op_choice_val < 30) operation_type = 1;
362
+ else if (op_choice_val < 60) operation_type = 2;
363
+ else if (op_choice_val < 70) operation_type = 3;
364
+ else if (op_choice_val < 85) operation_type = 4;
365
+ else operation_type = 5;
366
+
367
+ bool is_length_increasing_op = (operation_type == 0 || operation_type == 4);
368
+ // Op5 can increase or decrease length. Check its specific outcome for length control.
369
+ bool is_length_decreasing_op = (operation_type == 1 || operation_type == 2);
370
+
371
+ if (is_length_increasing_op && L_curr > MAX_L_PATH_HIGH_THRESHOLD_EFFECTIVE) {
372
+ if (accept_dist_01(RND_ENGINE) < 0.75) continue;
373
+ }
374
+ if (is_length_decreasing_op && L_curr < MIN_L_PATH_LOW_THRESHOLD_EFFECTIVE) {
375
+ if (accept_dist_01(RND_ENGINE) < 0.75) continue;
376
+ }
377
+
378
+
379
+ if (operation_type == 0) {
380
+ if (L_curr == 0 && N_GRID_SIZE > 1) continue;
381
+ if (candidate_pd_obj.moves.size() + 2 > MAX_L_PATH) continue;
382
+ if (current_pd_obj.coords.empty() && N_GRID_SIZE > 1) continue;
383
+
384
+ std::uniform_int_distribution<int> k_idx_dist(0, L_curr);
385
+ int k_coord_idx = k_idx_dist(RND_ENGINE);
386
+ Pos p_k = current_pd_obj.coords[k_coord_idx];
387
+
388
+ std::vector<int> possible_dirs; possible_dirs.reserve(4);
389
+ for(int dir_i=0; dir_i<4; ++dir_i) {
390
+ Pos neighbor_p = Pos{(int16_t)(p_k.r + DR[dir_i]), (int16_t)(p_k.c + DC[dir_i])};
391
+ if (is_valid_pos(neighbor_p.r, neighbor_p.c) && !check_wall(p_k, neighbor_p)) {
392
+ possible_dirs.push_back(dir_i);
393
+ }
394
+ }
395
+ if (possible_dirs.empty()) continue;
396
+ std::uniform_int_distribution<int> dir_choice_dist(0, possible_dirs.size()-1);
397
+ int random_dir_idx = possible_dirs[dir_choice_dist(RND_ENGINE)];
398
+
399
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + k_coord_idx,
400
+ {DIR_CHARS[random_dir_idx], DIR_CHARS[(random_dir_idx+2)%4]});
401
+ } else if (operation_type == 1) {
402
+ if (L_curr < 2) continue;
403
+ if (current_pd_obj.coords.size() < 3) continue;
404
+
405
+ std::vector<int> possible_indices; possible_indices.reserve(L_curr);
406
+ for(int k_m_idx = 0; k_m_idx <= L_curr - 2; ++k_m_idx) {
407
+ if (current_pd_obj.coords[k_m_idx] == current_pd_obj.coords[k_m_idx+2]) {
408
+ possible_indices.push_back(k_m_idx);
409
+ }
410
+ }
411
+ if (possible_indices.empty()) continue;
412
+ std::uniform_int_distribution<int> idx_choice_dist(0, possible_indices.size()-1);
413
+ int k_move_idx_to_remove = possible_indices[idx_choice_dist(RND_ENGINE)];
414
+
415
+ candidate_pd_obj.moves.erase(candidate_pd_obj.moves.begin() + k_move_idx_to_remove,
416
+ candidate_pd_obj.moves.begin() + k_move_idx_to_remove + 2);
417
+ } else if (operation_type == 2) {
418
+ if (L_curr < 1) continue;
419
+
420
+ std::uniform_int_distribution<int> c_idx1_dist(0, L_curr > 0 ? L_curr - 1 : 0);
421
+ int c_idx1 = c_idx1_dist(RND_ENGINE);
422
+ std::uniform_int_distribution<int> c_idx2_dist(c_idx1 + 1, L_curr);
423
+ int c_idx2 = c_idx2_dist(RND_ENGINE);
424
+ if (c_idx1 >= c_idx2 && L_curr > 0) continue; // Need valid subsegment
425
+ if (L_curr == 0 && (c_idx1!=0 || c_idx2!=0)) continue; // L=0 means c_idx1=0, c_idx2=0 only
426
+
427
+ Pos p_A = current_pd_obj.coords[c_idx1]; Pos p_B = current_pd_obj.coords[c_idx2];
428
+
429
+ if (!get_apsp_moves(p_A, p_B, GET_APSP_MOVES_BUFFER1)) continue;
430
+
431
+ if (GET_APSP_MOVES_BUFFER1.size() >= (size_t)(c_idx2 - c_idx1) && L_curr > 0 ) continue; // APSP not shorter (allow if L_curr=0)
432
+
433
+ if ( ( (long long)candidate_pd_obj.moves.size() - (c_idx2 - c_idx1) + GET_APSP_MOVES_BUFFER1.size()) > MAX_L_PATH) continue;
434
+
435
+ if (c_idx1 < c_idx2) { // Ensure erase range is valid
436
+ candidate_pd_obj.moves.erase(candidate_pd_obj.moves.begin() + c_idx1,
437
+ candidate_pd_obj.moves.begin() + c_idx2);
438
+ }
439
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + c_idx1,
440
+ GET_APSP_MOVES_BUFFER1.begin(), GET_APSP_MOVES_BUFFER1.end());
441
+ } else if (operation_type == 3) {
442
+ continue; // Disabled: reversing + inverting subsegments often breaks path; skip to save time.
443
+
444
+ } else if (operation_type == 4) {
445
+ if (L_curr == 0 && N_GRID_SIZE > 1) continue;
446
+ if (current_pd_obj.coords.empty() && N_GRID_SIZE > 1) continue;
447
+
448
+ Pos target_cell = select_target_cell_for_dirt_ops(current_pd_obj, false);
449
+
450
+ int best_k_coord_idx = -1;
451
+ long long min_detour_len_increase = (long long)MAX_L_PATH * 2 +1; // path len increase: 2 for wiggle, 2*dist for detour
452
+
453
+ if (L_curr >= 0) { // Path can be empty (L_curr=0), then coords has 1 element (0,0)
454
+ int num_samples = (L_curr == 0) ? 1: OP4_SAMPLE_POINTS; // If L_curr=0, only one point to pick: coords[0]
455
+ for (int i=0; i < num_samples; ++i) {
456
+ std::uniform_int_distribution<int> k_idx_dist(0, L_curr);
457
+ int k_coord_idx_sample = (L_curr == 0) ? 0 : k_idx_dist(RND_ENGINE);
458
+ Pos p_A_sample = current_pd_obj.coords[k_coord_idx_sample];
459
+
460
+ long long current_detour_increase;
461
+ if (p_A_sample == target_cell) {
462
+ current_detour_increase = 2; // Wiggle cost
463
+ } else {
464
+ int dist_pa_target = APSP_DIST[p_A_sample.r][p_A_sample.c][target_cell.r][target_cell.c];
465
+ if (dist_pa_target != -1) {
466
+ current_detour_increase = (long long)dist_pa_target * 2;
467
+ } else {
468
+ current_detour_increase = (long long)MAX_L_PATH * 2 + 1; // effectively infinity
469
+ }
470
+ }
471
+ if (current_detour_increase < min_detour_len_increase) {
472
+ min_detour_len_increase = current_detour_increase;
473
+ best_k_coord_idx = k_coord_idx_sample;
474
+ }
475
+ }
476
+ }
477
+
478
+
479
+ if (best_k_coord_idx == -1 || min_detour_len_increase > MAX_L_PATH) continue;
480
+
481
+ Pos p_A = current_pd_obj.coords[best_k_coord_idx];
482
+
483
+ if (candidate_pd_obj.moves.size() + min_detour_len_increase > MAX_L_PATH) continue;
484
+
485
+ if (p_A == target_cell) {
486
+ std::vector<int> possible_dirs; possible_dirs.reserve(4);
487
+ for(int dir_i=0; dir_i<4; ++dir_i) {
488
+ Pos neighbor_p = Pos{(int16_t)(p_A.r + DR[dir_i]), (int16_t)(p_A.c + DC[dir_i])};
489
+ if (is_valid_pos(neighbor_p.r, neighbor_p.c) && !check_wall(p_A, neighbor_p)) {
490
+ possible_dirs.push_back(dir_i);
491
+ }
492
+ }
493
+ if (possible_dirs.empty()) continue;
494
+ std::uniform_int_distribution<int> dir_choice_dist(0, possible_dirs.size()-1);
495
+ int random_dir_idx = possible_dirs[dir_choice_dist(RND_ENGINE)];
496
+
497
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + best_k_coord_idx,
498
+ {DIR_CHARS[random_dir_idx], DIR_CHARS[(random_dir_idx+2)%4]});
499
+ } else {
500
+ if (!get_apsp_moves(p_A, target_cell, GET_APSP_MOVES_BUFFER1)) continue;
501
+ if (!get_apsp_moves(target_cell, p_A, GET_APSP_MOVES_BUFFER2)) continue;
502
+
503
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + best_k_coord_idx,
504
+ GET_APSP_MOVES_BUFFER2.begin(), GET_APSP_MOVES_BUFFER2.end());
505
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + best_k_coord_idx,
506
+ GET_APSP_MOVES_BUFFER1.begin(), GET_APSP_MOVES_BUFFER1.end());
507
+ }
508
+ } else { // operation_type == 5:
509
+ Pos target_cell = select_target_cell_for_dirt_ops(current_pd_obj, true);
510
+
511
+ int c_idx1, c_idx2;
512
+ if (L_curr == 0) {
513
+ c_idx1 = 0; c_idx2 = 0;
514
+ } else {
515
+ std::uniform_int_distribution<int> c_idx1_dist_op5(0, L_curr -1 );
516
+ c_idx1 = c_idx1_dist_op5(RND_ENGINE);
517
+ std::uniform_int_distribution<int> c_idx2_dist_op5(c_idx1 + 1, std::min(L_curr, c_idx1 + OP5_MAX_SUBSEGMENT_LEN));
518
+ c_idx2 = c_idx2_dist_op5(RND_ENGINE);
519
+ }
520
+ if (c_idx1 > c_idx2) continue; // Should not happen with above logic for L_curr > 0
521
+
522
+ Pos p_A = current_pd_obj.coords[c_idx1];
523
+ Pos p_B = current_pd_obj.coords[c_idx2];
524
+
525
+ if (!get_apsp_moves(p_A, target_cell, GET_APSP_MOVES_BUFFER1)) continue;
526
+ if (!get_apsp_moves(target_cell, p_B, GET_APSP_MOVES_BUFFER2)) continue;
527
+
528
+ long long current_subsegment_len_moves = c_idx2 - c_idx1;
529
+ long long new_subsegment_len_moves = GET_APSP_MOVES_BUFFER1.size() + GET_APSP_MOVES_BUFFER2.size();
530
+
531
+ // Specific length control for Op5
532
+ if (new_subsegment_len_moves > current_subsegment_len_moves && L_curr > MAX_L_PATH_HIGH_THRESHOLD_EFFECTIVE) {
533
+ if (accept_dist_01(RND_ENGINE) < 0.75) continue;
534
+ }
535
+ if (new_subsegment_len_moves < current_subsegment_len_moves && L_curr < MIN_L_PATH_LOW_THRESHOLD_EFFECTIVE) {
536
+ if (accept_dist_01(RND_ENGINE) < 0.75) continue;
537
+ }
538
+
539
+
540
+ if ( ( (long long)candidate_pd_obj.moves.size() - current_subsegment_len_moves + new_subsegment_len_moves) > MAX_L_PATH) continue;
541
+
542
+ if (c_idx1 < c_idx2) {
543
+ candidate_pd_obj.moves.erase(candidate_pd_obj.moves.begin() + c_idx1,
544
+ candidate_pd_obj.moves.begin() + c_idx2);
545
+ }
546
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + c_idx1,
547
+ GET_APSP_MOVES_BUFFER2.begin(), GET_APSP_MOVES_BUFFER2.end());
548
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + c_idx1,
549
+ GET_APSP_MOVES_BUFFER1.begin(), GET_APSP_MOVES_BUFFER1.end());
550
+ }
551
+
552
+ modified_successfully = true;
553
+ break;
554
+ }
555
+
556
+ if (!modified_successfully) continue;
557
+
558
+ calculate_score_full(candidate_pd_obj);
559
+
560
+ bool candidate_is_invalid = candidate_pd_obj.score_val > 1e17L;
561
+
562
+ if (candidate_pd_obj.score_val < current_pd_obj.score_val) {
563
+ current_pd_obj = std::move(candidate_pd_obj);
564
+ if (current_pd_obj.score_val < best_pd_obj.score_val) {
565
+ best_pd_obj = current_pd_obj;
566
+ }
567
+ } else if (!candidate_is_invalid) {
568
+ auto now_time_temp = std::chrono::high_resolution_clock::now();
569
+ double elapsed_seconds_temp = std::chrono::duration<double>(now_time_temp - time_start_prog).count();
570
+ double progress_ratio = elapsed_seconds_temp / time_limit_seconds;
571
+ progress_ratio = std::min(1.0, std::max(0.0, progress_ratio));
572
+
573
+ double current_temp_val = end_temp;
574
+ if (start_temp > end_temp + 1e-9) {
575
+ current_temp_val = start_temp * std::pow(end_temp / start_temp, progress_ratio);
576
+ } else if (start_temp > 1e-9) {
577
+ current_temp_val = start_temp;
578
+ } else {
579
+ current_temp_val = end_temp;
580
+ }
581
+ if (current_temp_val < 1e-9 && end_temp >= 1e-9) current_temp_val = end_temp;
582
+ else if (current_temp_val < 1e-9) current_temp_val = 1e-9;
583
+
584
+ if (exp((current_pd_obj.score_val - candidate_pd_obj.score_val) / current_temp_val) > accept_dist_01(RND_ENGINE)) {
585
+ current_pd_obj = std::move(candidate_pd_obj);
586
+ }
587
+ }
588
+ }
589
+
590
+ for (char move_char : best_pd_obj.moves) std::cout << move_char;
591
+ std::cout << std::endl;
592
+
593
+ return 0;
594
+ }
595
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc027/config.yaml ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ALE-Bench ahc027 — AtCoder Heuristic Contest
2
+ # Usage: skydiscover-run initial_program.cpp evaluator.py -c config.yaml -s <strategy>
3
+ language: cpp
4
+ diff_based_generation: true
5
+ max_iterations: 100
6
+ checkpoint_interval: 10
7
+ max_solution_length: 60000
8
+ llm:
9
+ api_base: https://api.openai.com/v1
10
+ models:
11
+ - name: "gpt-5"
12
+ weight: 1.0
13
+ max_tokens: 32000
14
+ timeout: 600
15
+ prompt:
16
+ system_message: "You are a world-class algorithm engineer, and you are very good at programming. Now, you are participating\
17
+ \ in a programming contest. You are asked to solve a heuristic problem, known as an NP-hard problem.\n\nStory\n--------\n\
18
+ F Corporation developed a robotic vacuum cleaner, Takahashi-kun cleaner No.2, and decided to entrust it with the cleaning\
19
+ \ of their office.\nTakahashi-kun cleaner No.2 can operate indefinitely through solar power and repeats cleaning on a\
20
+ \ predetermined route indefinitely.\nThe office has varying levels of susceptibility to dirt in different areas, and by\
21
+ \ frequently cleaning the areas that are more prone to dirt, the entire office can be kept cleaner.\n\nProblem Statement\n\
22
+ --------\nThere is an $N\\times N$ square board.\nLet $(0, 0)$ be the coordinates of the top-left square, and $(i, j)$\
23
+ \ be the coordinates of the square located $i$ squares down and $j$ squares to the right from there.\nThe perimeter of\
24
+ \ the $N\\times N$ board is surrounded by walls, and there may also be walls between adjacent squares.\n\nEach square\
25
+ \ $(i,j)$ is assigned a value $d_{i,j}$ which represents its susceptibility to dirt.\nYour task is to clean these squares\
26
+ \ by moving around the board.\nYou can move to an adjacent square that is not blocked by a wall.\nAfter the move, the\
27
+ \ dirtiness of the square you moved to becomes $0$, and the dirtiness of all other squares $(i, j)$ increases by $d_{i,\
28
+ \ j}$.\nConsider a cleaning route that starts and ends at $(0, 0)$, with a length (number of moves) not exceeding $10^5$.\n\
29
+ The cleaning route may pass through the same square multiple times, but must visit each square at least once.\n\nLet $a_{t,i,j}$\
30
+ \ denote the dirtiness of each square $(i,j)$ after the $t$-th move, and let $S_t=\\sum_{i=0}^{N-1}\\sum_{j=0}^{N-1} a_{t,i,j}$\
31
+ \ denote the total dirtiness.\nAt $t=0$, we assume that the dirtiness of all squares is $a_{0,i,j}=0$.\nDefine the **average\
32
+ \ dirtiness** as\n\\\\[\n \\bar{S}=\\frac{1}{L}\\sum_{t=L}^{2L-1}S_t,\n\\\\]\nwhich is the average of the total dirtiness\
33
+ \ during the period $t=L,L+1,\\cdots,2L-1$ when the cleaning route of length $L$ is repeated infinitely.\n\nPlease find\
34
+ \ a cleaning route that minimizes the average dirtiness as much as possible.\n\n#### The Meaning of Average Dirtiness\
35
+ \ \nWe can prove that $a_{t,i,j}=a_{t+L,i,j}$ for $t\\geq L$ when the cleaning route of length $L$ is repeated infinitely.\n\
36
+ Therefore, considering the average $\\frac{1}{T} \\sum_{t=0}^{T-1} S_t$ of the total dirtiness up to $T$ turns, its limit\
37
+ \ as $T \\to \\infty$ coincides with the average dirtiness.\n\n\nScoring\n--------\nLet $\\bar{S}$ be the average dirtiness\
38
+ \ of the output cleaning route.\nThen you will obtain an absolute score of $\\mathrm{round}(\\bar{S})$.\nThe lower the\
39
+ \ absolute score, the better.\nIf you output an illegal cleaning route (length exceeds $10^5$, does not return to $(0,0)$,\
40
+ \ there is an unvisited square, or it hits a wall), it will be judged as <span class='label label-warning' data-toggle='tooltip'\
41
+ \ data-placement='top' title=\"Wrong Answer\">WA</span>.\n\nFor each test case, we compute the <font color=\"red\"><strong>relative\
42
+ \ score</strong></font> $\\mathrm{round}(10^9\\times \\frac{\\mathrm{MIN}}{\\mathrm{YOUR}})$, where YOUR is your absolute\
43
+ \ score and MIN is the lowest absolute score among all competitors obtained on that test case. The score of the submission\
44
+ \ is the sum of the relative scores.\n\nThe final ranking will be determined by the system test with more inputs which\
45
+ \ will be run after the contest is over.\nIn both the provisional/system test, if your submission produces illegal output\
46
+ \ or exceeds the time limit for some test cases, only the score for those test cases will be zero, and your submission\
47
+ \ will be excluded from the MIN calculation for those test cases.\n\nThe system test will be performed only for <font\
48
+ \ color=\"red\"><strong>the last submission which received a result other than <span class=\"label label-warning\" data-toggle=\"\
49
+ tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Compilation Error\">CE</span> </strong></font>.\nBe\
50
+ \ careful not to make a mistake in the final submission.\n\n\n#### Number of test cases\n- Provisional test: 50\n- System\
51
+ \ test: 2000. We will publish <a href=\"https://img.atcoder.jp/ahc027/seeds.txt\">seeds.txt</a> (sha256=cdea33a6050850bf1387e2191b802a1df7e43fcb969fd6c3bf9cbd96a4d790d7)\
52
+ \ after the contest is over.\n\n#### About relative evaluation system\nIn both the provisional/system test, the standings\
53
+ \ will be calculated using only the last submission which received a result other than <span class=\"label label-warning\"\
54
+ \ data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Compilation Error\">CE</span>.\nOnly\
55
+ \ the last submissions are used to calculate the MIN for each test case when calculating the relative scores.\n\nThe scores\
56
+ \ shown in the standings are relative, and whenever a new submission arrives, all relative scores are recalculated.\n\
57
+ On the other hand, the score for each submission shown on the submissions page is the sum of the absolute score for each\
58
+ \ test case, and the relative scores are not shown.\nIn order to know the relative score of submission other than the\
59
+ \ latest one in the current standings, you need to resubmit it.\nIf your submission produces illegal output or exceeds\
60
+ \ the time limit for some test cases, the score shown on the submissions page will be 0, but the standings show the sum\
61
+ \ of the relative scores for the test cases that were answered correctly.\n\n#### About execution time\nExecution time\
62
+ \ may vary slightly from run to run.\nIn addition, since system tests simultaneously perform a large number of executions,\
63
+ \ it has been observed that execution time increases by several percent compared to provisional tests.\nFor these reasons,\
64
+ \ submissions that are very close to the time limit may result in <span class='label label-warning' data-toggle='tooltip'\
65
+ \ data-placement='top' title=\"Time Limit Exceeded\">TLE</span> in the system test.\nPlease measure the execution time\
66
+ \ in your program to terminate the process, or have enough margin in the execution time.\n\n\nInput\n--------\nInput is\
67
+ \ given from Standard Input in the following format.\n\n~~~\n$N$\n$h_{0,0}\\cdots h_{0,N-1}$\n$\\vdots$\n$h_{N-2,0} \\\
68
+ cdots h_{N-2,N-1}$\n$v_{0,0} \\cdots v_{0,N-2}$\n$\\vdots$\n$v_{N-1,0} \\cdots v_{N-1,N-2}$\n$d_{0,0}$ $\\cdots$ $d_{0,N-1}$\n\
69
+ $\\vdots$\n$d_{N-1,0}$ $\\cdots$ $d_{N-1,N-1}$\n~~~\n\n- $N$ is the horizontal and vertical size of the board and satisfies\
70
+ \ $20\\leq N\\leq 40$.\n- $h_{i,0}\\cdots h_{i,N-1}$ is a string of length $N$ consisting of only `0` and `1`. $h_{i,j}=1$\
71
+ \ if and only if there is a wall between square $(i,j)$ and its lower neighbor $(i+1,j)$.\n- $v_{i,0}\\cdots v_{i,N-2}$\
72
+ \ is a string of length $N-1$ consisting of only `0` and `1`. $v_{i,j}=1$ if and only if there is a wall between square\
73
+ \ $(i,j)$ and its right neighbor $(i,j+1)$.\n- All squares are guaranteed to be reachable from $(0, 0)$.\n- $d_{i,j}$\
74
+ \ is an integer value representing the susceptibility to dirt of square $(i,j)$ and satisfies $1\\leq d_{i,j}\\leq 10^3$.\n\
75
+ \n\nOutput\n--------\nRepresent a move up, down, left, or right by `U`, `D`, `L`, or `R`, respectively.\nRepresent the\
76
+ \ cleaning route of length $L$ as a string of $L$ characters corresponding to each move, and output it in a single line\
77
+ \ to Standard Output.\n\n\n<a href=\"https://img.atcoder.jp/ahc027/aPdjCUIZ.html?lang=en&seed=0&output=sample\">Show example</a>\n\
78
+ \n\nSample Solution\n--------\n<details>\nThis is a sample solution in Python.\nIn this program, by moving along the depth-first\
79
+ \ search tree starting from (0,0), each edge in the tree is passed twice, once on the way there and once on the way back,\
80
+ \ and the program outputs a cleaning route that returns to (0,0).\n<pre class=\"prettyprint linenums\">\nimport sys\n\
81
+ sys.setrecursionlimit(1000000)\n\nN = int(input())\nh = [input() for _ in range(N-1)]\nv = [input() for _ in range(N)]\n\
82
+ d = [list(map(int, input().split())) for _ in range(N)]\n\nvisited = [[False for _ in range(N)] for _ in range(N)]\nDIJ\
83
+ \ = [(0, 1), (1, 0), (0, -1), (-1, 0)]\nDIR = \"RDLU\"\n\ndef dfs(i, j):\n visited[i][j] = True\n for dir in range(4):\n\
84
+ \ di, dj = DIJ[dir]\n i2 = i + di\n j2 = j + dj\n if 0 <= i2 < N and 0 <= j2 < N and not visited[i2][j2]:\n\
85
+ \ if di == 0 and v[i][min(j, j2)] == '0' or dj == 0 and h[min(i, i2)][j] == '0':\n print(DIR[dir], end='')\n\
86
+ \ dfs(i2, j2)\n print(DIR[(dir + 2) % 4], end='')\n\ndfs(0, 0)\nprint()\n</pre>\n</details>\n\n\nInput Generation\n\
87
+ --------\n<details>\nLet $\\mathrm{randint}(L,U)$ be a function that generates a uniform random integer between $L$ and\
88
+ \ $U$, inclusive.\nLet $\\mathrm{randdouble}(L,U)$ be a function that generates a uniform random floating-point number\
89
+ \ at least $L$ and less than $U$.\n\n#### Generation of $N$\n$N=\\mathrm{randint}(20,40)$.\n\n#### Generation of $h$ and\
90
+ \ $v$\nGenerate a parameter $w=\\mathrm{randint}(1,N)$ that controls the number of walls.\nStarting from a state with\
91
+ \ no walls, generate walls by repeating the following operation $w$ times.\n\nRandomly select one of the four directions\
92
+ \ (up, down, left, right).\nFor the left direction, generate $i=\\mathrm{randint}(0,N-2)$, $j=\\mathrm{randint}(0,N-1)$,\
93
+ \ and $k=\\mathrm{randint}(3,\\lfloor N/2\\rfloor)$.\nThen, set $h_{i,j}\\cdots h_{i,\\max(j-k+1, 0)}$ to $1$.\nSimilarly,\
94
+ \ for the right direction, generate values in the same manner, and set $h_{i,j}\\cdots h_{i,\\min(j+k-1, N-1)}$ to $1$.\n\
95
+ For the upward direction, generate $i=\\mathrm{randint}(0,N-1)$, $j=\\mathrm{randint}(0,N-2)$, and $k=\\mathrm{randint}(3,\\\
96
+ lfloor N/2\\rfloor)$.\nThen, set $v_{i,j}\\cdots v_{\\max(i-k+1, 0),j}$ to $1$.\nSimilarly, for the downward direction,\
97
+ \ generate values in the same manner, and set $v_{i,j}\\cdots v_{\\min(i+k-1, N-1),j}$ to $1$.\n\nAfter $w$ iterations\
98
+ \ are completed, check if all squares are reachable from $(0, 0)$, and if there are unreachable squares, remove all walls\
99
+ \ and redo the $w$ iterations.\n\n#### Generation of $d$\nGenerate a parameter $c=\\mathrm{randint}(1,\\lfloor N/2\\rfloor)$\
100
+ \ that determines the number of susceptible regions.\nCreate an array $d'$ with $d'_{i,j}=0$ for all $(i,j)$, and update\
101
+ \ $d'$ by repeating the following process $c$ times.\n\nGenerate $i=\\mathrm{randint}(0,N-1)$, $j=\\mathrm{randint}(0,N-1)$,\
102
+ \ $m=\\mathrm{randint}(N,\\lfloor N^2/c\\rfloor)$, and $b=\\mathrm{randdouble}(0,2)$.\nGenerate a set $S$ by starting\
103
+ \ from $S=\\\\{(i,j)\\\\}$ and repeating the following process until the size of $S$ becomes $m$.\n\nRandomly choose $p\\\
104
+ in S$, and randomly choose one of the four directions (up, down, left, or right). If there is no wall in that direction\
105
+ \ from $p$, add the adjacent square $q$ to $S$.\n\nFor each square $(i',j')\\in S$ contained in the generated $S$, overwrite\
106
+ \ $d'_{i',j'}=b$.\n\nAfter $c$ iterations are completed, generate $d_{i,j}=\\mathrm{round}(10^{d'_{i,j}+\\mathrm{randdouble}(0,1)})$\
107
+ \ for each $(i,j)$.\n</details>\n\nTools (Input generator and visualizer)\n--------\n- <a href=\"https://img.atcoder.jp/ahc027/aPdjCUIZ.html?lang=en\"\
108
+ >Web version</a>: This is more powerful than the local version providing animations.\n- <a href=\"https://img.atcoder.jp/ahc027/aPdjCUIZ_v2.zip\"\
109
+ >Local version</a>: You need a compilation environment of <a href=\"https://www.rust-lang.org/\">Rust language</a>.\n\
110
+ \ - <a href=\"https://img.atcoder.jp/ahc027/aPdjCUIZ_windows_v2.zip\">Pre-compiled binary for Windows</a>: If you are\
111
+ \ not familiar with the Rust language environment, please use this instead.\n\nPlease be aware that sharing visualization\
112
+ \ results or discussing solutions/ideas during the contest is prohibited.\n\n{sample example}\n\n\n Problem constraints:\n\
113
+ \ time_limit=2.0 memory_limit=1073741824\n"
114
+ evaluator:
115
+ timeout: 10000
116
+ cascade_evaluation: false
117
+
benchmarks/ale_bench/ale-bench-lite-problems/ahc027/evaluator.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+ from pathlib import Path
3
+ from ale_bench.result import CaseResult, JudgeResult, Result
4
+ from ale_bench_eval.safe_ale_session import start_ale_bench_session
5
+ import logging
6
+ import sys
7
+ logger = logging.getLogger(__name__ + "_" + "ALE_BENCH_EVALUATOR")
8
+
9
+ def result_feedback(result: Result) -> CaseResult:
10
+ if result.overall_judge_result == JudgeResult.ACCEPTED:
11
+ return result.case_results[0]
12
+ else:
13
+ selected_case_idx = 0
14
+ for idx, case_result in enumerate(result.case_results):
15
+ if case_result.judge_result == result.overall_judge_result:
16
+ selected_case_idx = idx
17
+ break
18
+ return result.case_results[selected_case_idx]
19
+
20
+ def evaluate(program_path):
21
+ problem_id = "ahc027"
22
+ logger.info(f"Evaluating program {program_path} for problem {problem_id} in ale bench evaluator")
23
+ try:
24
+ session = None
25
+ logger.info("Starting ALE-Bench session")
26
+ session = start_ale_bench_session(
27
+ problem_id=problem_id,
28
+ lite_version=True,
29
+ num_workers=13,
30
+ )
31
+ logger.info("ALE-Bench session started")
32
+ if not session:
33
+ raise RuntimeError("Failed to start or restart the session.")
34
+ optim_factor = 1 if session.problem.metadata.score_type == "maximize" else -1
35
+ code = Path(program_path).read_text().replace("# EVOLVE-BLOCK-START", "").replace("# EVOLVE-BLOCK-END", "").strip()
36
+ logger.info("Code extracted")
37
+ num_public_cases = 50
38
+ cases = session.case_gen(list(range(num_public_cases)))
39
+ public_result = session.case_eval(
40
+ cases, code, code_language="cpp20", skip_local_visualization=True
41
+ )
42
+ logger.info("Public evaluation completed")
43
+ extracted_case = result_feedback(public_result)
44
+ logger.info("Result feedback completed")
45
+ logger.info("ALE-Bench session closed")
46
+ combined_score = public_result.overall_absolute_score * optim_factor / num_public_cases
47
+ if public_result.overall_judge_result != JudgeResult.ACCEPTED and optim_factor == -1:
48
+ combined_score = -sys.maxsize - 1
49
+ session.close()
50
+ return {
51
+ "judge_result": public_result.overall_judge_result.value,
52
+ "overall_score": public_result.overall_absolute_score,
53
+ "max_execution_time_sec": max([case_result.execution_time for case_result in public_result.case_results]),
54
+ "max_memory_usage_mib": max([case_result.memory_usage for case_result in public_result.case_results]) // 1024 // 1024,
55
+ "standard_error": extracted_case.error_str,
56
+ "message": extracted_case.message,
57
+ "combined_score": combined_score,
58
+ }
59
+ except Exception as e:
60
+ logger.error(f"Evaluation failed completely: {str(e)}")
61
+ logger.error(traceback.format_exc())
62
+ return {
63
+ "overall_score": 0.0,
64
+ "error": str(e),
65
+ }
benchmarks/ale_bench/ale-bench-lite-problems/ahc027/initial_program.cpp ADDED
@@ -0,0 +1,614 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #pragma GCC optimize("O3,unroll-loops")
3
+
4
+ #include <iostream>
5
+ #include <vector>
6
+ #include <string>
7
+ #include <numeric>
8
+ #include <algorithm>
9
+ #include <chrono>
10
+ #include <random>
11
+ #include <iomanip>
12
+ #include <cmath>
13
+ // #include <map> // Not used
14
+
15
+ // Global game data
16
+ int N_GRID_SIZE;
17
+ std::vector<std::string> H_WALLS_INFO;
18
+ std::vector<std::string> V_WALLS_INFO;
19
+ int D_SUSC[40][40];
20
+
21
+ struct Pos {
22
+ int16_t r, c;
23
+ Pos() : r(0), c(0) {}
24
+ Pos(int16_t r_val, int16_t c_val) : r(r_val), c(c_val) {}
25
+
26
+ bool operator==(const Pos& other) const { return r == other.r && c == other.c; }
27
+ bool operator!=(const Pos& other) const { return !(*this == other); }
28
+ bool operator<(const Pos& other) const {
29
+ if (r != other.r) return r < other.r;
30
+ return c < other.c;
31
+ }
32
+ };
33
+
34
+ constexpr int DR[] = {0, 1, 0, -1}; // R, D, L, U
35
+ constexpr int DC[] = {1, 0, -1, 0};
36
+ constexpr char DIR_CHARS[] = {'R', 'D', 'L', 'U'};
37
+ const int MAX_L_PATH = 100000;
38
+ double MAX_L_PATH_HIGH_THRESHOLD_EFFECTIVE;
39
+ double MIN_L_PATH_LOW_THRESHOLD_EFFECTIVE;
40
+
41
+
42
+ std::mt19937 RND_ENGINE;
43
+
44
+ Pos APSP_PARENT[40][40][40][40];
45
+ int APSP_DIST[40][40][40][40];
46
+
47
+ bool is_valid_pos(int r, int c) {
48
+ return r >= 0 && r < N_GRID_SIZE && c >= 0 && c < N_GRID_SIZE;
49
+ }
50
+
51
+ bool check_wall(Pos p_from, Pos p_to) {
52
+ int dr = p_to.r - p_from.r;
53
+ int dc = p_to.c - p_from.c;
54
+ if (dr == 1) { // Down
55
+ return H_WALLS_INFO[p_from.r][p_from.c] == '1';
56
+ } else if (dr == -1) { // Up
57
+ return H_WALLS_INFO[p_to.r][p_to.c] == '1';
58
+ } else if (dc == 1) { // Right
59
+ return V_WALLS_INFO[p_from.r][p_from.c] == '1';
60
+ } else if (dc == -1) { // Left
61
+ return V_WALLS_INFO[p_from.r][p_to.c] == '1';
62
+ }
63
+ return true;
64
+ }
65
+
66
+ char get_move_char(Pos p_from, Pos p_to) {
67
+ int dr = p_to.r - p_from.r;
68
+ int dc = p_to.c - p_from.c;
69
+ for(int i=0; i<4; ++i) if(DR[i] == dr && DC[i] == dc) return DIR_CHARS[i];
70
+ return ' ';
71
+ }
72
+
73
+ char invert_move(char move_char) {
74
+ for(int i=0; i<4; ++i) if(DIR_CHARS[i] == move_char) return DIR_CHARS[(i+2)%4];
75
+ return ' ';
76
+ }
77
+
78
+ void compute_apsp() {
79
+ for (int sr = 0; sr < N_GRID_SIZE; ++sr) {
80
+ for (int sc = 0; sc < N_GRID_SIZE; ++sc) {
81
+ for (int tr = 0; tr < N_GRID_SIZE; ++tr) for (int tc = 0; tc < N_GRID_SIZE; ++tc) APSP_DIST[sr][sc][tr][tc] = -1;
82
+
83
+ std::vector<Pos> q; q.reserve(N_GRID_SIZE * N_GRID_SIZE);
84
+ q.push_back(Pos{(int16_t)sr, (int16_t)sc});
85
+ APSP_DIST[sr][sc][sr][sc] = 0;
86
+
87
+ int head = 0;
88
+ while(head < static_cast<int>(q.size())){
89
+ Pos curr = q[head++];
90
+ for(int i=0; i<4; ++i){
91
+ Pos next_candidate = Pos{(int16_t)(curr.r + DR[i]), (int16_t)(curr.c + DC[i])};
92
+ if(is_valid_pos(next_candidate.r, next_candidate.c) && !check_wall(curr, next_candidate) && APSP_DIST[sr][sc][next_candidate.r][next_candidate.c] == -1){
93
+ APSP_DIST[sr][sc][next_candidate.r][next_candidate.c] = APSP_DIST[sr][sc][curr.r][curr.c] + 1;
94
+ APSP_PARENT[sr][sc][next_candidate.r][next_candidate.c] = curr;
95
+ q.push_back(next_candidate);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ bool get_apsp_moves(Pos p_from, Pos p_to, std::vector<char>& out_moves) {
104
+ out_moves.clear();
105
+ if (p_from == p_to) return true;
106
+ if (APSP_DIST[p_from.r][p_from.c][p_to.r][p_to.c] == -1) return false;
107
+
108
+ out_moves.reserve(APSP_DIST[p_from.r][p_from.c][p_to.r][p_to.c]);
109
+ Pos curr = p_to;
110
+ while(curr != p_from) {
111
+ Pos prev = APSP_PARENT[p_from.r][p_from.c][curr.r][curr.c];
112
+ out_moves.push_back(get_move_char(prev, curr));
113
+ curr = prev;
114
+ }
115
+ std::reverse(out_moves.begin(), out_moves.end());
116
+ return true;
117
+ }
118
+
119
+ std::vector<std::vector<std::vector<int>>> CELL_VISIT_TIMES_GLOBAL_BUFFER;
120
+
121
+ struct CellDirtInfo {
122
+ long double weighted_dirt_contribution;
123
+ Pos p;
124
+ bool operator<(const CellDirtInfo& other) const {
125
+ return weighted_dirt_contribution > other.weighted_dirt_contribution;
126
+ }
127
+ };
128
+ std::vector<CellDirtInfo> TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER;
129
+
130
+ struct PathData {
131
+ std::vector<char> moves;
132
+ std::vector<Pos> coords;
133
+ bool visited_flags[40][40];
134
+ long double score_val;
135
+ long double total_dirt_sum_numerator;
136
+ long double cell_dirt_term_sum[40][40];
137
+
138
+ PathData() : score_val(1e18L), total_dirt_sum_numerator(0.0L) {
139
+ for(int i=0; i<N_GRID_SIZE; ++i) for(int j=0; j<N_GRID_SIZE; ++j) {
140
+ visited_flags[i][j] = false;
141
+ cell_dirt_term_sum[i][j] = 0.0L;
142
+ }
143
+ }
144
+
145
+ PathData(const PathData& other) = default;
146
+ PathData(PathData&& other) = default;
147
+ PathData& operator=(const PathData& other) = default;
148
+ PathData& operator=(PathData&& other) = default;
149
+ };
150
+
151
+ bool update_coords_and_visited_flags(PathData& pd) {
152
+ pd.coords.assign(1, Pos{0,0});
153
+ if (!pd.moves.empty()) {
154
+ pd.coords.reserve(pd.moves.size() + 1);
155
+ }
156
+
157
+ for (int r = 0; r < N_GRID_SIZE; ++r) for (int c = 0; c < N_GRID_SIZE; ++c) pd.visited_flags[r][c] = false;
158
+ pd.visited_flags[0][0] = true;
159
+
160
+ Pos current_p = Pos{0,0};
161
+ for (char move_char : pd.moves) {
162
+ int dir_idx = -1;
163
+ for (int i = 0; i < 4; ++i) if (DIR_CHARS[i] == move_char) dir_idx = i;
164
+
165
+ if (dir_idx == -1) return false;
166
+
167
+ Pos next_p = Pos{(int16_t)(current_p.r + DR[dir_idx]), (int16_t)(current_p.c + DC[dir_idx])};
168
+ if (!is_valid_pos(next_p.r, next_p.c) || check_wall(current_p, next_p)) return false;
169
+
170
+ current_p = next_p;
171
+ pd.coords.push_back(current_p);
172
+ pd.visited_flags[current_p.r][current_p.c] = true;
173
+ }
174
+ return true;
175
+ }
176
+
177
+ void calculate_score_full(PathData& pd) {
178
+ if (!update_coords_and_visited_flags(pd)) {
179
+ pd.score_val = 1e18L;
180
+ return;
181
+ }
182
+
183
+ if (pd.moves.size() > MAX_L_PATH) {
184
+ pd.score_val = 1e18L; return;
185
+ }
186
+ if (!pd.moves.empty()){
187
+ if (pd.coords.back() != Pos{0,0}) { pd.score_val = 1e18L; return;}
188
+ } else {
189
+ if (N_GRID_SIZE > 1) {
190
+ pd.score_val = 1e18L; return;
191
+ }
192
+ }
193
+
194
+ for (int r = 0; r < N_GRID_SIZE; ++r) for (int c = 0; c < N_GRID_SIZE; ++c) {
195
+ if (!pd.visited_flags[r][c]) { pd.score_val = 1e18L; return; }
196
+ }
197
+
198
+ int L = pd.moves.size();
199
+
200
+ if (L == 0) {
201
+ pd.score_val = (N_GRID_SIZE == 1) ? 0.0L : 1e18L; // N=1 case not in this contest
202
+ pd.total_dirt_sum_numerator = 0;
203
+ if (N_GRID_SIZE == 1) pd.cell_dirt_term_sum[0][0] = 0;
204
+ return;
205
+ }
206
+
207
+ for(int r=0; r<N_GRID_SIZE; ++r) {
208
+ for(int c=0; c<N_GRID_SIZE; ++c) {
209
+ CELL_VISIT_TIMES_GLOBAL_BUFFER[r][c].clear();
210
+ }
211
+ }
212
+
213
+ for (int t = 1; t <= L; ++t) {
214
+ Pos p = pd.coords[t];
215
+ CELL_VISIT_TIMES_GLOBAL_BUFFER[p.r][p.c].push_back(t);
216
+ }
217
+
218
+ pd.total_dirt_sum_numerator = 0;
219
+
220
+ for (int r_ = 0; r_ < N_GRID_SIZE; ++r_) {
221
+ for (int c_ = 0; c_ < N_GRID_SIZE; ++c_) {
222
+ const auto& specific_cell_visits = CELL_VISIT_TIMES_GLOBAL_BUFFER[r_][c_];
223
+ long double current_cell_dirt_term = 0;
224
+
225
+ if (!specific_cell_visits.empty()){
226
+ int num_visits_in_cycle = specific_cell_visits.size();
227
+ for (int i = 0; i < num_visits_in_cycle; ++i) {
228
+ long long prev_visit_t = (i == 0) ? ((long long)specific_cell_visits[num_visits_in_cycle - 1] - L) : (long long)specific_cell_visits[i-1];
229
+ long long cur_visit_t = specific_cell_visits[i];
230
+ long long delta = cur_visit_t - prev_visit_t;
231
+ current_cell_dirt_term += (long double)delta * (delta - 1) / 2.0L;
232
+ }
233
+ } else {
234
+ current_cell_dirt_term = (long double)L * (L - 1) / 2.0L;
235
+ }
236
+ pd.cell_dirt_term_sum[r_][c_] = current_cell_dirt_term;
237
+ pd.total_dirt_sum_numerator += (long double)D_SUSC[r_][c_] * current_cell_dirt_term;
238
+ }
239
+ }
240
+ pd.score_val = pd.total_dirt_sum_numerator / L;
241
+ }
242
+
243
+ bool initial_dfs_visited[40][40];
244
+ void generate_initial_dfs_path(int r, int c, PathData& pd) {
245
+ initial_dfs_visited[r][c] = true;
246
+ for (int dir_idx = 0; dir_idx < 4; ++dir_idx) {
247
+ Pos current_p = Pos{(int16_t)r, (int16_t)c};
248
+ Pos next_p = Pos{(int16_t)(r + DR[dir_idx]), (int16_t)(c + DC[dir_idx])};
249
+ if (is_valid_pos(next_p.r, next_p.c) && !check_wall(current_p, next_p) && !initial_dfs_visited[next_p.r][next_p.c]) {
250
+ pd.moves.push_back(DIR_CHARS[dir_idx]);
251
+ generate_initial_dfs_path(next_p.r, next_p.c, pd);
252
+ pd.moves.push_back(DIR_CHARS[(dir_idx + 2) % 4]);
253
+ }
254
+ }
255
+ }
256
+
257
+ Pos select_target_cell_for_dirt_ops(const PathData& current_pd_obj, bool use_sqrt_N_sampling) {
258
+ TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.clear();
259
+ if (current_pd_obj.score_val > 1e17L) {
260
+ std::uniform_int_distribution<int> r_dist(0, N_GRID_SIZE-1);
261
+ return Pos{(int16_t)r_dist(RND_ENGINE), (int16_t)r_dist(RND_ENGINE)};
262
+ }
263
+
264
+ for(int r=0; r<N_GRID_SIZE; ++r) {
265
+ for(int c=0; c<N_GRID_SIZE; ++c) {
266
+ TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.push_back({(long double)D_SUSC[r][c] * current_pd_obj.cell_dirt_term_sum[r][c], Pos{(int16_t)r,(int16_t)c}});
267
+ }
268
+ }
269
+ std::sort(TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.begin(), TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.end());
270
+
271
+ if (TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.empty()) {
272
+ std::uniform_int_distribution<int> r_dist(0, N_GRID_SIZE-1);
273
+ return Pos{(int16_t)r_dist(RND_ENGINE), (int16_t)r_dist(RND_ENGINE)};
274
+ }
275
+
276
+ int K_select;
277
+ if(use_sqrt_N_sampling){
278
+ K_select = std::min((int)TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.size(), std::max(1, N_GRID_SIZE));
279
+ } else {
280
+ K_select = std::min((int)TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.size(), std::max(10, N_GRID_SIZE * N_GRID_SIZE / 10));
281
+ }
282
+
283
+ if (K_select <= 0) {
284
+ std::uniform_int_distribution<int> r_dist(0, N_GRID_SIZE-1);
285
+ return Pos{(int16_t)r_dist(RND_ENGINE), (int16_t)r_dist(RND_ENGINE)};
286
+ }
287
+
288
+ std::uniform_int_distribution<int> top_k_dist(0, K_select - 1);
289
+ return TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER[top_k_dist(RND_ENGINE)].p;
290
+ }
291
+
292
+ const int OP4_SAMPLE_POINTS = 20;
293
+ const int OP5_MAX_SUBSEGMENT_LEN = 20;
294
+
295
+ std::vector<char> GET_APSP_MOVES_BUFFER1;
296
+ std::vector<char> GET_APSP_MOVES_BUFFER2;
297
+
298
+
299
+ int main(int argc, char *argv[]) {
300
+ std::ios_base::sync_with_stdio(false); std::cin.tie(NULL);
301
+
302
+ double time_limit_seconds = 1.95;
303
+ if (argc > 1) time_limit_seconds = std::stod(argv[1]);
304
+
305
+ auto time_start_prog = std::chrono::high_resolution_clock::now();
306
+ RND_ENGINE.seed(std::chrono::system_clock::now().time_since_epoch().count());
307
+
308
+ std::cin >> N_GRID_SIZE;
309
+ H_WALLS_INFO.resize(N_GRID_SIZE - 1);
310
+ V_WALLS_INFO.resize(N_GRID_SIZE);
311
+
312
+ for (int i = 0; i < N_GRID_SIZE - 1; ++i) std::cin >> H_WALLS_INFO[i];
313
+ for (int i = 0; i < N_GRID_SIZE; ++i) std::cin >> V_WALLS_INFO[i];
314
+ for (int i = 0; i < N_GRID_SIZE; ++i) for (int j = 0; j < N_GRID_SIZE; ++j) std::cin >> D_SUSC[i][j];
315
+
316
+ MAX_L_PATH_HIGH_THRESHOLD_EFFECTIVE = MAX_L_PATH * 0.95;
317
+ MIN_L_PATH_LOW_THRESHOLD_EFFECTIVE = N_GRID_SIZE * N_GRID_SIZE;
318
+
319
+ compute_apsp();
320
+
321
+ CELL_VISIT_TIMES_GLOBAL_BUFFER.resize(N_GRID_SIZE);
322
+ TMP_CELL_DIRT_INFOS_LIST_GLOBAL_BUFFER.reserve(N_GRID_SIZE * N_GRID_SIZE);
323
+ for(int r=0; r<N_GRID_SIZE; ++r) {
324
+ CELL_VISIT_TIMES_GLOBAL_BUFFER[r].resize(N_GRID_SIZE);
325
+ int reserve_size = std::max(2, MAX_L_PATH / (N_GRID_SIZE*N_GRID_SIZE) + 50);
326
+ if (reserve_size > MAX_L_PATH / 2 && MAX_L_PATH > 2) reserve_size = MAX_L_PATH/2; // Cap reasonable max
327
+ for(int c=0; c<N_GRID_SIZE; ++c) {
328
+ CELL_VISIT_TIMES_GLOBAL_BUFFER[r][c].reserve(reserve_size);
329
+ }
330
+ }
331
+ GET_APSP_MOVES_BUFFER1.reserve(N_GRID_SIZE*N_GRID_SIZE*2);
332
+ GET_APSP_MOVES_BUFFER2.reserve(N_GRID_SIZE*N_GRID_SIZE*2);
333
+
334
+
335
+ PathData current_pd_obj;
336
+ for(int i=0; i<N_GRID_SIZE; ++i) for(int j=0; j<N_GRID_SIZE; ++j) initial_dfs_visited[i][j] = false;
337
+ generate_initial_dfs_path(0, 0, current_pd_obj);
338
+ calculate_score_full(current_pd_obj);
339
+
340
+ PathData best_pd_obj = current_pd_obj;
341
+
342
+ double start_temp = 5000.0 * sqrt(N_GRID_SIZE);
343
+ double end_temp = 0.1;
344
+
345
+ int iterations_count = 0;
346
+ PathData candidate_pd_obj;
347
+ std::uniform_real_distribution<double> accept_dist_01(0.0, 1.0);
348
+
349
+
350
+ while(true) {
351
+ iterations_count++;
352
+ if(iterations_count % 100 == 0){
353
+ auto now_time = std::chrono::high_resolution_clock::now();
354
+ double elapsed_seconds = std::chrono::duration<double>(now_time - time_start_prog).count();
355
+ if (elapsed_seconds > time_limit_seconds) break;
356
+ }
357
+
358
+ int L_curr = current_pd_obj.moves.size();
359
+
360
+ bool modified_successfully = false;
361
+ for (int try_op = 0; try_op < 10; ++try_op) {
362
+ candidate_pd_obj = current_pd_obj;
363
+
364
+ std::uniform_int_distribution<int> op_dist(0, 99);
365
+ int op_choice_val = op_dist(RND_ENGINE);
366
+ int operation_type = -1;
367
+
368
+ if (op_choice_val < 15) operation_type = 0;
369
+ else if (op_choice_val < 30) operation_type = 1;
370
+ else if (op_choice_val < 60) operation_type = 2;
371
+ else if (op_choice_val < 70) operation_type = 3;
372
+ else if (op_choice_val < 85) operation_type = 4;
373
+ else operation_type = 5;
374
+
375
+ bool is_length_increasing_op = (operation_type == 0 || operation_type == 4);
376
+ // Op5 can increase or decrease length. Check its specific outcome for length control.
377
+ bool is_length_decreasing_op = (operation_type == 1 || operation_type == 2);
378
+
379
+ if (is_length_increasing_op && L_curr > MAX_L_PATH_HIGH_THRESHOLD_EFFECTIVE) {
380
+ if (accept_dist_01(RND_ENGINE) < 0.75) continue;
381
+ }
382
+ if (is_length_decreasing_op && L_curr < MIN_L_PATH_LOW_THRESHOLD_EFFECTIVE) {
383
+ if (accept_dist_01(RND_ENGINE) < 0.75) continue;
384
+ }
385
+
386
+
387
+ if (operation_type == 0) {
388
+ if (L_curr == 0 && N_GRID_SIZE > 1) continue;
389
+ if (candidate_pd_obj.moves.size() + 2 > MAX_L_PATH) continue;
390
+ if (current_pd_obj.coords.empty() && N_GRID_SIZE > 1) continue;
391
+
392
+ std::uniform_int_distribution<int> k_idx_dist(0, L_curr);
393
+ int k_coord_idx = k_idx_dist(RND_ENGINE);
394
+ Pos p_k = current_pd_obj.coords[k_coord_idx];
395
+
396
+ std::vector<int> possible_dirs; possible_dirs.reserve(4);
397
+ for(int dir_i=0; dir_i<4; ++dir_i) {
398
+ Pos neighbor_p = Pos{(int16_t)(p_k.r + DR[dir_i]), (int16_t)(p_k.c + DC[dir_i])};
399
+ if (is_valid_pos(neighbor_p.r, neighbor_p.c) && !check_wall(p_k, neighbor_p)) {
400
+ possible_dirs.push_back(dir_i);
401
+ }
402
+ }
403
+ if (possible_dirs.empty()) continue;
404
+ std::uniform_int_distribution<int> dir_choice_dist(0, possible_dirs.size()-1);
405
+ int random_dir_idx = possible_dirs[dir_choice_dist(RND_ENGINE)];
406
+
407
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + k_coord_idx,
408
+ {DIR_CHARS[random_dir_idx], DIR_CHARS[(random_dir_idx+2)%4]});
409
+ } else if (operation_type == 1) {
410
+ if (L_curr < 2) continue;
411
+ if (current_pd_obj.coords.size() < 3) continue;
412
+
413
+ std::vector<int> possible_indices; possible_indices.reserve(L_curr);
414
+ for(int k_m_idx = 0; k_m_idx <= L_curr - 2; ++k_m_idx) {
415
+ if (current_pd_obj.coords[k_m_idx] == current_pd_obj.coords[k_m_idx+2]) {
416
+ possible_indices.push_back(k_m_idx);
417
+ }
418
+ }
419
+ if (possible_indices.empty()) continue;
420
+ std::uniform_int_distribution<int> idx_choice_dist(0, possible_indices.size()-1);
421
+ int k_move_idx_to_remove = possible_indices[idx_choice_dist(RND_ENGINE)];
422
+
423
+ candidate_pd_obj.moves.erase(candidate_pd_obj.moves.begin() + k_move_idx_to_remove,
424
+ candidate_pd_obj.moves.begin() + k_move_idx_to_remove + 2);
425
+ } else if (operation_type == 2) {
426
+ if (L_curr < 1) continue;
427
+
428
+ std::uniform_int_distribution<int> c_idx1_dist(0, L_curr > 0 ? L_curr - 1 : 0);
429
+ int c_idx1 = c_idx1_dist(RND_ENGINE);
430
+ std::uniform_int_distribution<int> c_idx2_dist(c_idx1 + 1, L_curr);
431
+ int c_idx2 = c_idx2_dist(RND_ENGINE);
432
+ if (c_idx1 >= c_idx2 && L_curr > 0) continue; // Need valid subsegment
433
+ if (L_curr == 0 && (c_idx1!=0 || c_idx2!=0)) continue; // L=0 means c_idx1=0, c_idx2=0 only
434
+
435
+ Pos p_A = current_pd_obj.coords[c_idx1]; Pos p_B = current_pd_obj.coords[c_idx2];
436
+
437
+ if (!get_apsp_moves(p_A, p_B, GET_APSP_MOVES_BUFFER1)) continue;
438
+
439
+ if (GET_APSP_MOVES_BUFFER1.size() >= (size_t)(c_idx2 - c_idx1) && L_curr > 0 ) continue; // APSP not shorter (allow if L_curr=0)
440
+
441
+ if ( ( (long long)candidate_pd_obj.moves.size() - (c_idx2 - c_idx1) + GET_APSP_MOVES_BUFFER1.size()) > MAX_L_PATH) continue;
442
+
443
+ if (c_idx1 < c_idx2) { // Ensure erase range is valid
444
+ candidate_pd_obj.moves.erase(candidate_pd_obj.moves.begin() + c_idx1,
445
+ candidate_pd_obj.moves.begin() + c_idx2);
446
+ }
447
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + c_idx1,
448
+ GET_APSP_MOVES_BUFFER1.begin(), GET_APSP_MOVES_BUFFER1.end());
449
+ } else if (operation_type == 3) {
450
+ if (L_curr < 1) continue;
451
+
452
+ std::uniform_int_distribution<int> move_idx_dist(0, L_curr -1);
453
+ int move_idx1 = move_idx_dist(RND_ENGINE);
454
+ std::uniform_int_distribution<int> move_idx_dist2(move_idx1, L_curr -1);
455
+ int move_idx2_inclusive = move_idx_dist2(RND_ENGINE);
456
+
457
+ int move_idx2_exclusive = move_idx2_inclusive + 1;
458
+
459
+ std::reverse(candidate_pd_obj.moves.begin() + move_idx1, candidate_pd_obj.moves.begin() + move_idx2_exclusive);
460
+ for(int i = move_idx1; i < move_idx2_exclusive; ++i)
461
+ candidate_pd_obj.moves[i] = invert_move(candidate_pd_obj.moves[i]);
462
+
463
+ } else if (operation_type == 4) {
464
+ if (L_curr == 0 && N_GRID_SIZE > 1) continue;
465
+ if (current_pd_obj.coords.empty() && N_GRID_SIZE > 1) continue;
466
+
467
+ Pos target_cell = select_target_cell_for_dirt_ops(current_pd_obj, false);
468
+
469
+ int best_k_coord_idx = -1;
470
+ long long min_detour_len_increase = (long long)MAX_L_PATH * 2 +1; // path len increase: 2 for wiggle, 2*dist for detour
471
+
472
+ if (L_curr >= 0) { // Path can be empty (L_curr=0), then coords has 1 element (0,0)
473
+ int num_samples = (L_curr == 0) ? 1: OP4_SAMPLE_POINTS; // If L_curr=0, only one point to pick: coords[0]
474
+ for (int i=0; i < num_samples; ++i) {
475
+ std::uniform_int_distribution<int> k_idx_dist(0, L_curr);
476
+ int k_coord_idx_sample = (L_curr == 0) ? 0 : k_idx_dist(RND_ENGINE);
477
+ Pos p_A_sample = current_pd_obj.coords[k_coord_idx_sample];
478
+
479
+ long long current_detour_increase;
480
+ if (p_A_sample == target_cell) {
481
+ current_detour_increase = 2; // Wiggle cost
482
+ } else {
483
+ int dist_pa_target = APSP_DIST[p_A_sample.r][p_A_sample.c][target_cell.r][target_cell.c];
484
+ if (dist_pa_target != -1) {
485
+ current_detour_increase = (long long)dist_pa_target * 2;
486
+ } else {
487
+ current_detour_increase = (long long)MAX_L_PATH * 2 + 1; // effectively infinity
488
+ }
489
+ }
490
+ if (current_detour_increase < min_detour_len_increase) {
491
+ min_detour_len_increase = current_detour_increase;
492
+ best_k_coord_idx = k_coord_idx_sample;
493
+ }
494
+ }
495
+ }
496
+
497
+
498
+ if (best_k_coord_idx == -1 || min_detour_len_increase > MAX_L_PATH) continue;
499
+
500
+ Pos p_A = current_pd_obj.coords[best_k_coord_idx];
501
+
502
+ if (candidate_pd_obj.moves.size() + min_detour_len_increase > MAX_L_PATH) continue;
503
+
504
+ if (p_A == target_cell) {
505
+ std::vector<int> possible_dirs; possible_dirs.reserve(4);
506
+ for(int dir_i=0; dir_i<4; ++dir_i) {
507
+ Pos neighbor_p = Pos{(int16_t)(p_A.r + DR[dir_i]), (int16_t)(p_A.c + DC[dir_i])};
508
+ if (is_valid_pos(neighbor_p.r, neighbor_p.c) && !check_wall(p_A, neighbor_p)) {
509
+ possible_dirs.push_back(dir_i);
510
+ }
511
+ }
512
+ if (possible_dirs.empty()) continue;
513
+ std::uniform_int_distribution<int> dir_choice_dist(0, possible_dirs.size()-1);
514
+ int random_dir_idx = possible_dirs[dir_choice_dist(RND_ENGINE)];
515
+
516
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + best_k_coord_idx,
517
+ {DIR_CHARS[random_dir_idx], DIR_CHARS[(random_dir_idx+2)%4]});
518
+ } else {
519
+ if (!get_apsp_moves(p_A, target_cell, GET_APSP_MOVES_BUFFER1)) continue;
520
+ if (!get_apsp_moves(target_cell, p_A, GET_APSP_MOVES_BUFFER2)) continue;
521
+
522
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + best_k_coord_idx,
523
+ GET_APSP_MOVES_BUFFER2.begin(), GET_APSP_MOVES_BUFFER2.end());
524
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + best_k_coord_idx,
525
+ GET_APSP_MOVES_BUFFER1.begin(), GET_APSP_MOVES_BUFFER1.end());
526
+ }
527
+ } else { // operation_type == 5:
528
+ Pos target_cell = select_target_cell_for_dirt_ops(current_pd_obj, true);
529
+
530
+ int c_idx1, c_idx2;
531
+ if (L_curr == 0) {
532
+ c_idx1 = 0; c_idx2 = 0;
533
+ } else {
534
+ std::uniform_int_distribution<int> c_idx1_dist_op5(0, L_curr -1 );
535
+ c_idx1 = c_idx1_dist_op5(RND_ENGINE);
536
+ std::uniform_int_distribution<int> c_idx2_dist_op5(c_idx1 + 1, std::min(L_curr, c_idx1 + OP5_MAX_SUBSEGMENT_LEN));
537
+ c_idx2 = c_idx2_dist_op5(RND_ENGINE);
538
+ }
539
+ if (c_idx1 > c_idx2) continue; // Should not happen with above logic for L_curr > 0
540
+
541
+ Pos p_A = current_pd_obj.coords[c_idx1];
542
+ Pos p_B = current_pd_obj.coords[c_idx2];
543
+
544
+ if (!get_apsp_moves(p_A, target_cell, GET_APSP_MOVES_BUFFER1)) continue;
545
+ if (!get_apsp_moves(target_cell, p_B, GET_APSP_MOVES_BUFFER2)) continue;
546
+
547
+ long long current_subsegment_len_moves = c_idx2 - c_idx1;
548
+ long long new_subsegment_len_moves = GET_APSP_MOVES_BUFFER1.size() + GET_APSP_MOVES_BUFFER2.size();
549
+
550
+ // Specific length control for Op5
551
+ if (new_subsegment_len_moves > current_subsegment_len_moves && L_curr > MAX_L_PATH_HIGH_THRESHOLD_EFFECTIVE) {
552
+ if (accept_dist_01(RND_ENGINE) < 0.75) continue;
553
+ }
554
+ if (new_subsegment_len_moves < current_subsegment_len_moves && L_curr < MIN_L_PATH_LOW_THRESHOLD_EFFECTIVE) {
555
+ if (accept_dist_01(RND_ENGINE) < 0.75) continue;
556
+ }
557
+
558
+
559
+ if ( ( (long long)candidate_pd_obj.moves.size() - current_subsegment_len_moves + new_subsegment_len_moves) > MAX_L_PATH) continue;
560
+
561
+ if (c_idx1 < c_idx2) {
562
+ candidate_pd_obj.moves.erase(candidate_pd_obj.moves.begin() + c_idx1,
563
+ candidate_pd_obj.moves.begin() + c_idx2);
564
+ }
565
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + c_idx1,
566
+ GET_APSP_MOVES_BUFFER2.begin(), GET_APSP_MOVES_BUFFER2.end());
567
+ candidate_pd_obj.moves.insert(candidate_pd_obj.moves.begin() + c_idx1,
568
+ GET_APSP_MOVES_BUFFER1.begin(), GET_APSP_MOVES_BUFFER1.end());
569
+ }
570
+
571
+ modified_successfully = true;
572
+ break;
573
+ }
574
+
575
+ if (!modified_successfully) continue;
576
+
577
+ calculate_score_full(candidate_pd_obj);
578
+
579
+ bool candidate_is_invalid = candidate_pd_obj.score_val > 1e17L;
580
+
581
+ if (candidate_pd_obj.score_val < current_pd_obj.score_val) {
582
+ current_pd_obj = std::move(candidate_pd_obj);
583
+ if (current_pd_obj.score_val < best_pd_obj.score_val) {
584
+ best_pd_obj = current_pd_obj;
585
+ }
586
+ } else if (!candidate_is_invalid) {
587
+ auto now_time_temp = std::chrono::high_resolution_clock::now();
588
+ double elapsed_seconds_temp = std::chrono::duration<double>(now_time_temp - time_start_prog).count();
589
+ double progress_ratio = elapsed_seconds_temp / time_limit_seconds;
590
+ progress_ratio = std::min(1.0, std::max(0.0, progress_ratio));
591
+
592
+ double current_temp_val = end_temp;
593
+ if (start_temp > end_temp + 1e-9) {
594
+ current_temp_val = start_temp * std::pow(end_temp / start_temp, progress_ratio);
595
+ } else if (start_temp > 1e-9) {
596
+ current_temp_val = start_temp;
597
+ } else {
598
+ current_temp_val = end_temp;
599
+ }
600
+ if (current_temp_val < 1e-9 && end_temp >= 1e-9) current_temp_val = end_temp;
601
+ else if (current_temp_val < 1e-9) current_temp_val = 1e-9;
602
+
603
+ if (exp((current_pd_obj.score_val - candidate_pd_obj.score_val) / current_temp_val) > accept_dist_01(RND_ENGINE)) {
604
+ current_pd_obj = std::move(candidate_pd_obj);
605
+ }
606
+ }
607
+ }
608
+
609
+ for (char move_char : best_pd_obj.moves) std::cout << move_char;
610
+ std::cout << std::endl;
611
+
612
+ return 0;
613
+ }
614
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale-bench-lite-problems/ahc039/config.yaml ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ALE-Bench ahc039 — AtCoder Heuristic Contest
2
+ # Usage: skydiscover-run initial_program.cpp evaluator.py -c config.yaml -s <strategy>
3
+ language: cpp
4
+ diff_based_generation: true
5
+ max_iterations: 100
6
+ checkpoint_interval: 10
7
+ max_solution_length: 60000
8
+ llm:
9
+ api_base: https://api.openai.com/v1
10
+ models:
11
+ - name: "gpt-5"
12
+ weight: 1.0
13
+ max_tokens: 32000
14
+ timeout: 600
15
+ prompt:
16
+ system_message: "You are a world-class algorithm engineer, and you are very good at programming. Now, you are participating\
17
+ \ in a programming contest. You are asked to solve a heuristic problem, known as an NP-hard problem.\n\nStory\n--------\n\
18
+ Takahashi is a skilled purse seine fisher.\nHis fishing boat is equipped with state-of-the-art sonar, allowing him to\
19
+ \ accurately determine the positions of fish within the fishing area.\nAdditionally, the boat is capable of high-speed\
20
+ \ movement, enabling him to assume that fish remain stationary while he sets up the fishing net.\n\nThe fishing method\
21
+ \ involves using the boat to deploy nets and form a closed polygon, capturing the fish within the enclosed area.\nTo optimize\
22
+ \ efficiency, each edge of the polygon formed by the nets must be aligned either parallel to the east-west or north-south\
23
+ \ direction.\nFurthermore, due to the limited length of the nets equipped on the boat, the polygon must be constructed\
24
+ \ within these constraints.\n\nThe fishing area contains two types of fish: mackerels and sardines.\nFor resource conservation\
25
+ \ reasons, sardines are currently prohibited from being caught in this fishing area.\nAny sardines caught in the net must\
26
+ \ be released back into the sea.\nBecause this process is labor-intensive, Takahashi should focus on maximizing the catch\
27
+ \ of mackerel while avoiding sardines as much as possible.\n\n\nProblem Statement\n--------\nThere are $N$ mackerels and\
28
+ \ $N$ sardines on a two-dimensional plane.\nConstruct a polygon that satisfies the following conditions and maximize the\
29
+ \ value obtained by subtracting the total number of sardines inside the polygon from the total number of mackerels inside\
30
+ \ it.\nNote that any points lying on the edges of the polygon are considered to be inside the polygon.\n\n### Conditions\n\
31
+ 1. The number of vertices in the polygon must not exceed $1000$, and the total length of its edges must not exceed $4\
32
+ \ \\times 10^5$.\n2. The coordinates of each vertex $(x, y)$ must be integers satisfying $0 \\leq x, y \\leq 10^5$.\n\
33
+ 3. Each edge of the polygon must be parallel to either the $x$-axis or the $y$-axis.\n4. The polygon must not self-intersect:\
34
+ \ non-adjacent edges must not share any points, and adjacent edges must only meet at their endpoints.\n\n\n\nScoring\n\
35
+ --------\nLet $a$ be the total number of mackerels inside the polygon and $b$ be the total number of sardines inside the\
36
+ \ polygon.\nThen, you will obtain the score of $\\max(0, a - b + 1)$.\n\nThere are $150$ test cases, and the score of\
37
+ \ a submission is the total score for each test case.\nIf your submission produces an illegal output or exceeds the time\
38
+ \ limit for some test cases, the submission itself will be judged as <span class='label label-warning' data-toggle='tooltip'\
39
+ \ data-placement='top' title=\"Wrong Answer\">WA</span> or <span class='label label-warning' data-toggle='tooltip' data-placement='top'\
40
+ \ title=\"Time Limit Exceeded\">TLE</span> , and the score of the submission will be zero.\nThe highest score obtained\
41
+ \ during the contest will determine the final ranking, and there will be no system test after the contest.\nIf more than\
42
+ \ one participant gets the same score, they will be ranked in the same place regardless of the submission time.\n\n\n\n\
43
+ Input\n--------\nInput is given from Standard Input in the following format:\n~~~\n$N$\n$x_0$ $y_0$\n$\\vdots$\n$x_{2N-1}$\
44
+ \ $y_{2N-1}$\n~~~\n\n- In all test cases, the number of mackerels and sardines, $N$, is fixed at $5000$.\n- For each $i\
45
+ \ = 0, 1, \\dots, N-1$, $(x_i, y_i)$ represents the coordinates of the $i$-th mackerel.\n- For each $i = 0, 1, \\dots,\
46
+ \ N-1$, $(x_{N+i}, y_{N+i})$ represents the coordinates of the $i$-th sardine.\n- Each coordinate $(x_i, y_i)$ satisfies\
47
+ \ $0 \\leq x_i, y_i \\leq 10^5$, and all coordinates are distinct.\n\n\nOutput\n--------\nLet the number of vertices in\
48
+ \ the polygon be $m$ ($4 \\leq m \\leq 1000$), and let $(a_i, b_i)$ denote the coordinates of the $i$-th vertex.\nThen,\
49
+ \ output to Standard Output in the following format:\n~~~\n$m$\n$a_0$ $b_0$\n$\\vdots$\n$a_{m-1}$ $b_{m-1}$\n~~~\n\nThe\
50
+ \ output vertices do not necessarily need to form the actual corners of the polygon.\nIn other words, three consecutive\
51
+ \ vertices $(a_i, b_i), (a_{i+1}, b_{i+1}), (a_{i+2}, b_{i+2})$ may lie on a straight line.\nHowever, all vertices must\
52
+ \ have distinct coordinates.\n\nThe vertices can be output in either clockwise or counterclockwise order.\n\n<a href=\"\
53
+ https://img.atcoder.jp/ahc039/KNtTkgAy.html?lang=en&seed=0&output=sample\">Show example</a>\n\n\nYour program may output\
54
+ \ multiple solutions.\nIf multiple solutions are output, only the last one is used for scoring.\nYou can compare multiple\
55
+ \ solutions using the web version of the visualizer.\n\n\n\n\n\nInput Generation\n--------\n- $\\mathrm{rand}(L, U)$:\
56
+ \ Generates a random integer uniformly distributed between $L$ and $U$ (inclusive).\n- $\\mathrm{rand\\\\_double}(L, U)$:\
57
+ \ Generates a random real number uniformly distributed between $L$ and $U$.\n- $\\mathrm{normal}(\\mu, \\sigma)$: Generates\
58
+ \ a random real number from a normal distribution with mean $\\mu$ and standard deviation $\\sigma$.\n\nFirst, generate\
59
+ \ the coordinates of mackerels.\nThe number of clusters $n$ is determined by generating $n = \\mathrm{rand}(10, 25)$.\n\
60
+ For each cluster $i$, generate the following parameters:\n\n- Weight $w_i = \\mathrm{rand\\\\_double}(0, 1)$\n- Center\
61
+ \ $(cx_i, cy_i) = (\\mathrm{rand}(20000, 80000), \\mathrm{rand}(20000, 80000))$\n- Standard deviation $\\sigma_i = \\\
62
+ mathrm{rand}(1000, 5000)$\n\nRepeat the following process $N$ times to generate the coordinates of $N$ mackerels:\n\n\
63
+ - Randomly select a cluster $i$ with probability proportional to its weight $w_i$.\n- Generate $x = \\mathrm{round}(\\\
64
+ mathrm{normal}(cx_i, \\sigma_i))$ and $y = \\mathrm{round}(\\mathrm{normal}(cy_i, \\sigma_i))$.\n- If the generated coordinates\
65
+ \ $(x, y)$ satisfy $0 \\leq x, y \\leq 10^5$ and are distinct from all previously generated coordinates, they are accepted\
66
+ \ as the coordinates of a mackerel. Otherwise, regenerate $(x, y)$.\n\nAfter generating the coordinates of mackerels,\
67
+ \ generate the coordinates of sardines in the same way.\n\n\n\nTools (Input generator and visualizer)\n--------\n- <a\
68
+ \ href=\"https://img.atcoder.jp/ahc039/KNtTkgAy.html?lang=en\">Web version</a>: This is more powerful than the local version\
69
+ \ providing animations.\n- <a href=\"https://img.atcoder.jp/ahc039/KNtTkgAy.zip\">Local version</a>: You need a compilation\
70
+ \ environment of <a href=\"https://www.rust-lang.org/\">Rust language</a>.\n - <a href=\"https://img.atcoder.jp/ahc039/KNtTkgAy_windows.zip\"\
71
+ >Pre-compiled binary for Windows</a>: If you are not familiar with the Rust language environment, please use this instead.\n\
72
+ \nPlease be aware that sharing visualization results or discussing solutions/ideas during the contest is prohibited.\n\
73
+ \n\n Problem constraints:\n time_limit=2.0 memory_limit=1073741824\n"
74
+ evaluator:
75
+ timeout: 10000
76
+ cascade_evaluation: false
77
+
benchmarks/ale_bench/ale-bench-lite-problems/ahc039/evaluator.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+ from pathlib import Path
3
+ from ale_bench.result import CaseResult, JudgeResult, Result
4
+ from ale_bench_eval.safe_ale_session import start_ale_bench_session
5
+ import logging
6
+ import sys
7
+ logger = logging.getLogger(__name__ + "_" + "ALE_BENCH_EVALUATOR")
8
+
9
+ def result_feedback(result: Result) -> CaseResult:
10
+ if result.overall_judge_result == JudgeResult.ACCEPTED:
11
+ return result.case_results[0]
12
+ else:
13
+ selected_case_idx = 0
14
+ for idx, case_result in enumerate(result.case_results):
15
+ if case_result.judge_result == result.overall_judge_result:
16
+ selected_case_idx = idx
17
+ break
18
+ return result.case_results[selected_case_idx]
19
+
20
+ def evaluate(program_path):
21
+ problem_id = "ahc039"
22
+ logger.info(f"Evaluating program {program_path} for problem {problem_id} in ale bench evaluator")
23
+ try:
24
+ session = None
25
+ logger.info("Starting ALE-Bench session")
26
+ session = start_ale_bench_session(
27
+ problem_id=problem_id,
28
+ lite_version=True,
29
+ num_workers=13,
30
+ )
31
+ logger.info("ALE-Bench session started")
32
+ if not session:
33
+ raise RuntimeError("Failed to start or restart the session.")
34
+ optim_factor = 1 if session.problem.metadata.score_type == "maximize" else -1
35
+ code = Path(program_path).read_text().replace("# EVOLVE-BLOCK-START", "").replace("# EVOLVE-BLOCK-END", "").strip()
36
+ logger.info("Code extracted")
37
+ num_public_cases = 50
38
+ cases = session.case_gen(list(range(num_public_cases)))
39
+ public_result = session.case_eval(
40
+ cases, code, code_language="cpp20", skip_local_visualization=True
41
+ )
42
+ logger.info("Public evaluation completed")
43
+ extracted_case = result_feedback(public_result)
44
+ logger.info("Result feedback completed")
45
+ logger.info("ALE-Bench session closed")
46
+ combined_score = public_result.overall_absolute_score * optim_factor / num_public_cases
47
+ if public_result.overall_judge_result != JudgeResult.ACCEPTED and optim_factor == -1:
48
+ combined_score = -sys.maxsize - 1
49
+ session.close()
50
+ return {
51
+ "judge_result": public_result.overall_judge_result.value,
52
+ "overall_score": public_result.overall_absolute_score,
53
+ "max_execution_time_sec": max([case_result.execution_time for case_result in public_result.case_results]),
54
+ "max_memory_usage_mib": max([case_result.memory_usage for case_result in public_result.case_results]) // 1024 // 1024,
55
+ "standard_error": extracted_case.error_str,
56
+ "message": extracted_case.message,
57
+ "combined_score": combined_score,
58
+ }
59
+ except Exception as e:
60
+ logger.error(f"Evaluation failed completely: {str(e)}")
61
+ logger.error(traceback.format_exc())
62
+ return {
63
+ "overall_score": 0.0,
64
+ "error": str(e),
65
+ }
benchmarks/ale_bench/ale-bench-lite-problems/ahc046/config.yaml ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ALE-Bench ahc046 — AtCoder Heuristic Contest
2
+ # Usage: skydiscover-run initial_program.cpp evaluator.py -c config.yaml -s <strategy>
3
+ language: cpp
4
+ diff_based_generation: true
5
+ max_iterations: 100
6
+ checkpoint_interval: 10
7
+ max_solution_length: 60000
8
+ llm:
9
+ api_base: https://api.openai.com/v1
10
+ models:
11
+ - name: "gpt-5"
12
+ weight: 1.0
13
+ max_tokens: 32000
14
+ timeout: 600
15
+ prompt:
16
+ system_message: "You are a world-class algorithm engineer, and you are very good at programming. Now, you are participating\
17
+ \ in a programming contest. You are asked to solve a heuristic problem, known as an NP-hard problem.\n\nProblem Statement\n\
18
+ --------\nThere is a skating rink consisting of $N \\times N$ squares.\nLet $(0, 0)$ be the coordinates of the top-left\
19
+ \ square, and $(i, j)$ be the coordinates of the square located $i$ squares down and $j$ squares to the right from there.\n\
20
+ All squares outside the $N \\times N$ area are covered with blocks and are impassable.\nInitially, there are no blocks\
21
+ \ inside the $N \\times N$ area.\n\nYou start at the initial position $(i_0, j_0)$ and must visit the specified target\
22
+ \ squares $(i_1, j_1), \\dots, (i_{M-1}, j_{M-1})$ in the given order.\n\nAt each turn, you may choose one of the four\
23
+ \ cardinal directions and perform one of the following actions:\n\n- **Move**: Move one square in the specified direction.\
24
+ \ You cannot move into a square containing a block.\n- **Slide**: Continue sliding in the specified direction until you\
25
+ \ hit a block.\n- **Alter**: Place a block on the adjacent square in the specified direction if it does not already contain\
26
+ \ one; otherwise, remove the existing block.\n You may not specify a square outside the $N \\times N$ area.\n It is\
27
+ \ also allowed to place a block on a current or future target square; however, you must remove the block in order to visit\
28
+ \ that square.\n\nIf you slide over a target square without stopping on it, it is **not** considered visited.\nA target\
29
+ \ square is considered visited only if you either stop on it after a **Slide**, or move onto it directly via a **Move**.\n\
30
+ \nYou must visit the target squares in the given order.\nEven if you pass over a later target square before visiting earlier\
31
+ \ ones, it is not considered visited at that time. You will need to visit it again when its turn in the sequence arrives.\n\
32
+ \nYou may perform at most $2NM$ actions.\nVisit all target squares in the specified order using as few turns as possible.\n\
33
+ \nScoring\n--------\nLet $T$ be the length of the output action sequence, and $m$ be the number of target squares successfully\
34
+ \ visited.\nThen, you will obtain the following score.\n\n- If $m<M-1$, $m+1$\n- If $m=M-1$, $M+2NM-T$\n\nThere are $150$\
35
+ \ test cases, and the score of a submission is the total score for each test case.\nIf your submission produces an illegal\
36
+ \ output or exceeds the time limit for some test cases, the submission itself will be judged as <span class='label label-warning'\
37
+ \ data-toggle='tooltip' data-placement='top' title=\"Wrong Answer\">WA</span> or <span class='label label-warning' data-toggle='tooltip'\
38
+ \ data-placement='top' title=\"Time Limit Exceeded\">TLE</span> , and the score of the submission will be zero.\nThe highest\
39
+ \ score obtained during the contest will determine the final ranking, and there will be no system test after the contest.\n\
40
+ If more than one participant gets the same score, they will be ranked in the same place regardless of the submission time.\n\
41
+ \n\nInput\n--------\nInput is given from Standard Input in the following format:\n\n~~~\n$N$ $M$\n$i_0$ $j_0$\n$\\vdots$\n\
42
+ $i_{M-1}$ $j_{M-1}$\n~~~\n\n- In all test cases, $N = 20$ and $M = 40$ are fixed.\n- The coordinates $(i_k, j_k)$ of the\
43
+ \ initial position and each target square are integers satisfying $0 \\leq i_k, j_k \\leq N-1$, and all coordinates are\
44
+ \ distinct.\n\n\nOutput\n--------\nAt each turn, represent the selected action and direction using a single uppercase\
45
+ \ alphabet letter as follows.\n\n**Actions**\n\n- Move: `M`\n- Slide: `S`\n- Alter: `A`\n\n**Directions**\n\n- Up: `U`\n\
46
+ - Down: `D`\n- Left: `L`\n- Right: `R`\n\nLet $a_t$ and $d_t$ denote the action and direction selected at turn $t$ ($t\
47
+ \ = 0, 1, \\dots, T-1$), respectively. \nThen, output to Standard Output in the following format:\n~~~\n$a_0$ $d_0$\n\
48
+ $\\vdots$\n$a_{T-1}$ $d_{T-1}$\n~~~\n\n\n<a href=\"https://img.atcoder.jp/ahc046/EuNd3uow.html?lang=en&seed=0&output=sample\"\
49
+ >Show example</a>\n\n\n\nInput Generation\n--------\nThe initial position and the target squares are generated according\
50
+ \ to the following procedure.\n\nFirst, randomly shuffle the coordinates of all $N^2$ squares.\nThen, take the first $M$\
51
+ \ coordinates from the shuffled list and assign them sequentially as $(i_0, j_0), (i_1, j_1), \\dots, (i_{M-1}, j_{M-1})$.\n\
52
+ \n\nTools (Input generator and visualizer)\n--------\n- <a href=\"https://img.atcoder.jp/ahc046/EuNd3uow.html?lang=en\"\
53
+ >Web version</a>: This is more powerful than the local version providing animations and manual play.\n- <a href=\"https://img.atcoder.jp/ahc046/EuNd3uow.zip\"\
54
+ >Local version</a>: You need a compilation environment of <a href=\"https://www.rust-lang.org/\">Rust language</a>.\n\
55
+ \ - <a href=\"https://img.atcoder.jp/ahc046/EuNd3uow_windows.zip\">Pre-compiled binary for Windows</a>: If you are not\
56
+ \ familiar with the Rust language environment, please use this instead.\n\nPlease be aware that sharing visualization\
57
+ \ results or discussing solutions/ideas during the contest is prohibited.\n\n{sample example}\n\n\n Problem constraints:\n\
58
+ time_limit=2.0 memory_limit=1073741824\n"
59
+ evaluator:
60
+ timeout: 10000
61
+ cascade_evaluation: false
62
+
benchmarks/ale_bench/ale-bench-lite-problems/ahc046/evaluator.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+ from pathlib import Path
3
+ from ale_bench.result import CaseResult, JudgeResult, Result
4
+ from ale_bench_eval.safe_ale_session import start_ale_bench_session
5
+ import logging
6
+ import sys
7
+ logger = logging.getLogger(__name__ + "_" + "ALE_BENCH_EVALUATOR")
8
+
9
+ def result_feedback(result: Result) -> CaseResult:
10
+ if result.overall_judge_result == JudgeResult.ACCEPTED:
11
+ return result.case_results[0]
12
+ else:
13
+ selected_case_idx = 0
14
+ for idx, case_result in enumerate(result.case_results):
15
+ if case_result.judge_result == result.overall_judge_result:
16
+ selected_case_idx = idx
17
+ break
18
+ return result.case_results[selected_case_idx]
19
+
20
+ def evaluate(program_path):
21
+ problem_id = "ahc046"
22
+ logger.info(f"Evaluating program {program_path} for problem {problem_id} in ale bench evaluator")
23
+ try:
24
+ session = None
25
+ logger.info("Starting ALE-Bench session")
26
+ session = start_ale_bench_session(
27
+ problem_id=problem_id,
28
+ lite_version=True,
29
+ num_workers=13,
30
+ )
31
+ logger.info("ALE-Bench session started")
32
+ if not session:
33
+ raise RuntimeError("Failed to start or restart the session.")
34
+ optim_factor = 1 if session.problem.metadata.score_type == "maximize" else -1
35
+ code = Path(program_path).read_text().replace("# EVOLVE-BLOCK-START", "").replace("# EVOLVE-BLOCK-END", "").strip()
36
+ logger.info("Code extracted")
37
+ num_public_cases = 50
38
+ cases = session.case_gen(list(range(num_public_cases)))
39
+ public_result = session.case_eval(
40
+ cases, code, code_language="cpp20", skip_local_visualization=True
41
+ )
42
+ logger.info("Public evaluation completed")
43
+ extracted_case = result_feedback(public_result)
44
+ logger.info("Result feedback completed")
45
+ logger.info("ALE-Bench session closed")
46
+ combined_score = public_result.overall_absolute_score * optim_factor / num_public_cases
47
+ if public_result.overall_judge_result != JudgeResult.ACCEPTED and optim_factor == -1:
48
+ combined_score = -sys.maxsize - 1
49
+ session.close()
50
+ return {
51
+ "judge_result": public_result.overall_judge_result.value,
52
+ "overall_score": public_result.overall_absolute_score,
53
+ "max_execution_time_sec": max([case_result.execution_time for case_result in public_result.case_results]),
54
+ "max_memory_usage_mib": max([case_result.memory_usage for case_result in public_result.case_results]) // 1024 // 1024,
55
+ "standard_error": extracted_case.error_str,
56
+ "message": extracted_case.message,
57
+ "combined_score": combined_score,
58
+ }
59
+ except Exception as e:
60
+ logger.error(f"Evaluation failed completely: {str(e)}")
61
+ logger.error(traceback.format_exc())
62
+ return {
63
+ "overall_score": 0.0,
64
+ "error": str(e),
65
+ }
benchmarks/ale_bench/ale-bench-lite-problems/ahc046/initial_program.cpp ADDED
@@ -0,0 +1,897 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include <string>
5
+ #include <queue>
6
+ #include <algorithm>
7
+ #include <tuple>
8
+ #include <array>
9
+ #include <chrono>
10
+ #include <random>
11
+ #include <cmath> // For std::exp, std::pow
12
+ #include <numeric> // For std::iota
13
+
14
+ // Constants
15
+ const int N_GRID = 20;
16
+ const int M_TARGETS_INPUT = 40;
17
+ const int NUM_SEGMENTS = M_TARGETS_INPUT - 1;
18
+ const int INF_COST = 1e9;
19
+ const int MAX_TOTAL_TURNS = 2 * N_GRID * M_TARGETS_INPUT; // 2*20*40 = 1600
20
+
21
+ // Randomness
22
+ unsigned int RND_SEED = std::chrono::steady_clock::now().time_since_epoch().count();
23
+ std::mt19937 rng(RND_SEED);
24
+
25
+ // Coordinates
26
+ struct Pos {
27
+ int r, c;
28
+ bool operator==(const Pos& other) const { return r == other.r && c == other.c; }
29
+ bool operator!=(const Pos& other) const { return !(*this == other); }
30
+ bool operator<(const Pos& other) const {
31
+ if (r != other.r) return r < other.r;
32
+ return c < other.c;
33
+ }
34
+ };
35
+ const Pos INVALID_POS = {-1, -1};
36
+
37
+ // Grid state
38
+ using Grid = std::array<std::array<bool, N_GRID>, N_GRID>; // true if block exists
39
+
40
+ bool is_valid_pos(Pos p) {
41
+ return p.r >= 0 && p.r < N_GRID && p.c >= 0 && p.c < N_GRID;
42
+ }
43
+
44
+ bool is_blocked_pos(Pos p, const Grid& grid) {
45
+ if (!is_valid_pos(p)) return true;
46
+ return grid[p.r][p.c];
47
+ }
48
+
49
+ void toggle_block_pos(Pos p, Grid& grid) {
50
+ if (is_valid_pos(p)) {
51
+ grid[p.r][p.c] = !grid[p.r][p.c];
52
+ }
53
+ }
54
+
55
+ // Directions
56
+ const int DR[] = {-1, 1, 0, 0}; // U, D, L, R
57
+ const int DC[] = {0, 0, -1, 1};
58
+ const char DIR_CHARS[] = {'U', 'D', 'L', 'R'};
59
+ const int DIR_REV_IDX[] = {1, 0, 3, 2}; // U(0)<->D(1), L(2)<->R(3)
60
+
61
+ // Global BFS structures for optimization
62
+ unsigned int g_bfs_generation_id = 0;
63
+ std::array<std::array<unsigned int, N_GRID>, N_GRID> g_bfs_cell_last_visited_generation;
64
+ std::array<std::array<int, N_GRID>, N_GRID> g_bfs_cell_dist;
65
+
66
+
67
+ std::string reconstruct_path_from_came_from(Pos start_pos, Pos dest_pos,
68
+ const std::array<std::array<std::pair<Pos, std::pair<char, char>>, N_GRID>, N_GRID>& came_from_data) {
69
+ std::string path_actions_str_reversed = ""; Pos p_trace = dest_pos;
70
+ while(p_trace != start_pos && is_valid_pos(p_trace)) {
71
+ auto const& action_info = came_from_data[p_trace.r][p_trace.c];
72
+ path_actions_str_reversed += action_info.second.second;
73
+ path_actions_str_reversed += action_info.second.first;
74
+ p_trace = action_info.first;
75
+ }
76
+ std::reverse(path_actions_str_reversed.begin(), path_actions_str_reversed.end());
77
+ return path_actions_str_reversed;
78
+ }
79
+
80
+ struct BFSResult { int cost; std::string actions_str; };
81
+
82
+ BFSResult bfs(Pos start_pos, Pos dest_pos, const Grid& grid, Pos intermediate_target_to_avoid, bool avoid_intermediate_target, bool build_action_str) {
83
+ g_bfs_generation_id++;
84
+
85
+ std::array<std::array<std::pair<Pos, std::pair<char, char>>, N_GRID>, N_GRID> came_from_local;
86
+ std::queue<Pos> q;
87
+
88
+ if (!is_valid_pos(start_pos)) return {INF_COST, ""};
89
+ if (avoid_intermediate_target && start_pos == intermediate_target_to_avoid && start_pos != dest_pos) {
90
+ return {INF_COST, ""};
91
+ }
92
+
93
+ g_bfs_cell_last_visited_generation[start_pos.r][start_pos.c] = g_bfs_generation_id;
94
+ g_bfs_cell_dist[start_pos.r][start_pos.c] = 0;
95
+ q.push(start_pos);
96
+
97
+ int min_dist_to_dest = INF_COST;
98
+ if (start_pos == dest_pos) min_dist_to_dest = 0;
99
+
100
+ while(!q.empty()){
101
+ Pos curr = q.front();
102
+ q.pop();
103
+
104
+ int d = g_bfs_cell_dist[curr.r][curr.c];
105
+
106
+ if (curr == dest_pos) {
107
+ min_dist_to_dest = std::min(min_dist_to_dest, d);
108
+ }
109
+
110
+ if (min_dist_to_dest != INF_COST && d >= min_dist_to_dest && curr != dest_pos) continue;
111
+ if (d + 1 > N_GRID * N_GRID) continue;
112
+
113
+ for (int i = 0; i < 4; ++i) { // Moves
114
+ Pos next_p = {curr.r + DR[i], curr.c + DC[i]};
115
+ if (is_blocked_pos(next_p, grid)) continue;
116
+ if (avoid_intermediate_target && next_p == intermediate_target_to_avoid && next_p != dest_pos) continue;
117
+
118
+ bool visited_in_current_bfs = (g_bfs_cell_last_visited_generation[next_p.r][next_p.c] == g_bfs_generation_id);
119
+ if (!visited_in_current_bfs || g_bfs_cell_dist[next_p.r][next_p.c] > d + 1) {
120
+ g_bfs_cell_last_visited_generation[next_p.r][next_p.c] = g_bfs_generation_id;
121
+ g_bfs_cell_dist[next_p.r][next_p.c] = d + 1;
122
+ if (build_action_str) came_from_local[next_p.r][next_p.c] = {curr, {'M', DIR_CHARS[i]}};
123
+ q.push(next_p);
124
+ }
125
+ }
126
+
127
+ for (int i = 0; i < 4; ++i) { // Slides
128
+ Pos current_slide_p = curr; Pos landed_at_p = curr;
129
+ while (true) {
130
+ Pos next_tile_in_slide = {current_slide_p.r + DR[i], current_slide_p.c + DC[i]};
131
+ if (is_blocked_pos(next_tile_in_slide, grid)) { landed_at_p = current_slide_p; break; }
132
+ if (avoid_intermediate_target && next_tile_in_slide == intermediate_target_to_avoid && next_tile_in_slide != dest_pos) {
133
+ landed_at_p = curr;
134
+ break;
135
+ }
136
+ current_slide_p = next_tile_in_slide;
137
+ }
138
+
139
+ if (landed_at_p == curr) continue;
140
+ Pos next_p = landed_at_p;
141
+
142
+ bool visited_in_current_bfs = (g_bfs_cell_last_visited_generation[next_p.r][next_p.c] == g_bfs_generation_id);
143
+ if (!visited_in_current_bfs || g_bfs_cell_dist[next_p.r][next_p.c] > d + 1) {
144
+ g_bfs_cell_last_visited_generation[next_p.r][next_p.c] = g_bfs_generation_id;
145
+ g_bfs_cell_dist[next_p.r][next_p.c] = d + 1;
146
+ if (build_action_str) came_from_local[next_p.r][next_p.c] = {curr, {'S', DIR_CHARS[i]}};
147
+ q.push(next_p);
148
+ }
149
+ }
150
+ }
151
+
152
+ BFSResult res = {INF_COST, ""};
153
+ if (is_valid_pos(dest_pos) && g_bfs_cell_last_visited_generation[dest_pos.r][dest_pos.c] == g_bfs_generation_id) {
154
+ res.cost = g_bfs_cell_dist[dest_pos.r][dest_pos.c];
155
+ if (build_action_str && res.cost != INF_COST) {
156
+ res.actions_str = reconstruct_path_from_came_from(start_pos, dest_pos, came_from_local);
157
+ }
158
+ }
159
+ return res;
160
+ }
161
+
162
+ void bfs_all(Pos start_pos, const Grid& grid,
163
+ Pos intermediate_target_to_avoid, bool strictly_avoid_intermediate,
164
+ std::array<std::array<int, N_GRID>, N_GRID>& dist_out,
165
+ std::array<std::array<std::pair<Pos, std::pair<char, char>>, N_GRID>, N_GRID>& came_from_out,
166
+ bool store_came_from) {
167
+
168
+ g_bfs_generation_id++;
169
+ std::queue<Pos> q;
170
+
171
+ for (int r_idx=0; r_idx<N_GRID; ++r_idx) std::fill(dist_out[r_idx].begin(), dist_out[r_idx].end(), INF_COST);
172
+
173
+ if (!is_valid_pos(start_pos)) return;
174
+ if (strictly_avoid_intermediate && start_pos == intermediate_target_to_avoid) {
175
+ return;
176
+ }
177
+
178
+ g_bfs_cell_last_visited_generation[start_pos.r][start_pos.c] = g_bfs_generation_id;
179
+ g_bfs_cell_dist[start_pos.r][start_pos.c] = 0;
180
+ q.push(start_pos);
181
+
182
+ while(!q.empty()){
183
+ Pos curr = q.front();
184
+ q.pop();
185
+ int d = g_bfs_cell_dist[curr.r][curr.c];
186
+
187
+ if (d + 1 > N_GRID * N_GRID) continue;
188
+
189
+ for (int i = 0; i < 4; ++i) { // Moves
190
+ Pos next_p = {curr.r + DR[i], curr.c + DC[i]};
191
+ if (is_blocked_pos(next_p, grid)) continue;
192
+ if (strictly_avoid_intermediate && next_p == intermediate_target_to_avoid) continue;
193
+
194
+ bool visited_in_current_bfs = (g_bfs_cell_last_visited_generation[next_p.r][next_p.c] == g_bfs_generation_id);
195
+ if (!visited_in_current_bfs || g_bfs_cell_dist[next_p.r][next_p.c] > d + 1) {
196
+ g_bfs_cell_last_visited_generation[next_p.r][next_p.c] = g_bfs_generation_id;
197
+ g_bfs_cell_dist[next_p.r][next_p.c] = d + 1;
198
+ if (store_came_from) came_from_out[next_p.r][next_p.c] = {curr, {'M', DIR_CHARS[i]}};
199
+ q.push(next_p);
200
+ }
201
+ }
202
+
203
+ for (int i = 0; i < 4; ++i) { // Slides
204
+ Pos current_slide_p = curr;
205
+ Pos landed_at_p = curr;
206
+ while (true) {
207
+ Pos next_tile_in_slide = {current_slide_p.r + DR[i], current_slide_p.c + DC[i]};
208
+ if (is_blocked_pos(next_tile_in_slide, grid)) {
209
+ landed_at_p = current_slide_p;
210
+ break;
211
+ }
212
+ if (strictly_avoid_intermediate && next_tile_in_slide == intermediate_target_to_avoid) {
213
+ landed_at_p = curr;
214
+ break;
215
+ }
216
+ current_slide_p = next_tile_in_slide;
217
+ }
218
+
219
+ if (landed_at_p == curr) continue;
220
+ Pos next_p = landed_at_p;
221
+
222
+ bool visited_in_current_bfs = (g_bfs_cell_last_visited_generation[next_p.r][next_p.c] == g_bfs_generation_id);
223
+ if (!visited_in_current_bfs || g_bfs_cell_dist[next_p.r][next_p.c] > d + 1) {
224
+ g_bfs_cell_last_visited_generation[next_p.r][next_p.c] = g_bfs_generation_id;
225
+ g_bfs_cell_dist[next_p.r][next_p.c] = d + 1;
226
+ if (store_came_from) came_from_out[next_p.r][next_p.c] = {curr, {'S', DIR_CHARS[i]}};
227
+ q.push(next_p);
228
+ }
229
+ }
230
+ }
231
+ for (int r_idx = 0; r_idx < N_GRID; ++r_idx) {
232
+ for (int c_idx = 0; c_idx < N_GRID; ++c_idx) {
233
+ if (g_bfs_cell_last_visited_generation[r_idx][c_idx] == g_bfs_generation_id) {
234
+ dist_out[r_idx][c_idx] = g_bfs_cell_dist[r_idx][c_idx];
235
+ }
236
+ }
237
+ }
238
+ }
239
+
240
+ Pos G_initial_pos;
241
+ std::vector<Pos> G_targets_vec;
242
+
243
+ struct SegmentExecResult { int turns = INF_COST; std::string actions_str; };
244
+
245
+ bool apply_direct_path_strat(Pos cur_P, Pos target_P, const Grid& g, SegmentExecResult& res, bool build_action_str) {
246
+ if (is_blocked_pos(target_P, g)) return false;
247
+ BFSResult bfs_res = bfs(cur_P, target_P, g, INVALID_POS, false, build_action_str);
248
+ if (bfs_res.cost == INF_COST) return false;
249
+ res.turns = bfs_res.cost;
250
+ if(build_action_str) res.actions_str = bfs_res.actions_str; else res.actions_str.clear();
251
+ return true;
252
+ }
253
+
254
+ bool apply_unblock_and_go_strat(Pos cur_P, Pos target_P, Grid& g , SegmentExecResult& res, bool build_action_str) {
255
+ if (!is_blocked_pos(target_P, g)) return false;
256
+
257
+ std::array<std::array<int, N_GRID>, N_GRID> dist_from_cur_P;
258
+ std::array<std::array<std::pair<Pos, std::pair<char, char>>, N_GRID>, N_GRID> came_from_data;
259
+
260
+ bfs_all(cur_P, g, target_P, true, dist_from_cur_P, came_from_data, build_action_str);
261
+
262
+ Pos best_adj_P = INVALID_POS;
263
+ int cost_to_best_adj_P = INF_COST;
264
+ char alter_dir_char_to_unblock = ' ';
265
+
266
+ for (int i=0; i<4; ++i) {
267
+ Pos adj_P = {target_P.r + DR[i], target_P.c + DC[i]};
268
+ if (!is_valid_pos(adj_P) || is_blocked_pos(adj_P, g)) continue;
269
+
270
+ if (dist_from_cur_P[adj_P.r][adj_P.c] < cost_to_best_adj_P) {
271
+ cost_to_best_adj_P = dist_from_cur_P[adj_P.r][adj_P.c];
272
+ best_adj_P = adj_P;
273
+ alter_dir_char_to_unblock = DIR_CHARS[DIR_REV_IDX[i]];
274
+ }
275
+ }
276
+
277
+ if (best_adj_P == INVALID_POS || cost_to_best_adj_P == INF_COST) return false;
278
+
279
+ res.turns = cost_to_best_adj_P + 1 + 1;
280
+ if (build_action_str) {
281
+ res.actions_str = reconstruct_path_from_came_from(cur_P, best_adj_P, came_from_data);
282
+ res.actions_str += 'A'; res.actions_str += alter_dir_char_to_unblock;
283
+ res.actions_str += 'M'; res.actions_str += alter_dir_char_to_unblock;
284
+ } else {
285
+ res.actions_str.clear();
286
+ }
287
+ toggle_block_pos(target_P, g);
288
+ return true;
289
+ }
290
+
291
+ bool apply_slide_strat(Pos cur_P, Pos target_P, Grid& g , SegmentExecResult& res, int slide_dir_idx, int type, bool build_action_str) {
292
+ if (is_blocked_pos(target_P, g)) return false;
293
+
294
+ int slide_dr = DR[slide_dir_idx], slide_dc = DC[slide_dir_idx];
295
+ char slide_dir_char = DIR_CHARS[slide_dir_idx];
296
+
297
+ Pos slide_start_P = {target_P.r - slide_dr, target_P.c - slide_dc};
298
+ Pos block_at_P = {target_P.r + slide_dr, target_P.c + slide_dc};
299
+
300
+ if (!is_valid_pos(slide_start_P)) return false;
301
+ if (slide_start_P == target_P) return false;
302
+
303
+ if (type == 0) {
304
+ bool wall_exists_for_slide = !is_valid_pos(block_at_P) || is_blocked_pos(block_at_P, g);
305
+ if (!wall_exists_for_slide) return false;
306
+
307
+ BFSResult path_to_slide_start_P = bfs(cur_P, slide_start_P, g,
308
+ target_P, true, build_action_str);
309
+ if (path_to_slide_start_P.cost == INF_COST) return false;
310
+
311
+ res.turns = path_to_slide_start_P.cost + 1;
312
+ if (build_action_str) {
313
+ res.actions_str = path_to_slide_start_P.actions_str;
314
+ res.actions_str += 'S'; res.actions_str += slide_dir_char;
315
+ } else {
316
+ res.actions_str.clear();
317
+ }
318
+ return true;
319
+
320
+ } else if (type == 1) {
321
+ if (!is_valid_pos(block_at_P)) return false;
322
+ if (is_blocked_pos(block_at_P, g)) return false;
323
+
324
+ BFSResult path_cur_to_target_P = bfs(cur_P, target_P, g, INVALID_POS, false, build_action_str);
325
+ if (path_cur_to_target_P.cost == INF_COST) return false;
326
+
327
+ Grid g_after_alter = g;
328
+ toggle_block_pos(block_at_P, g_after_alter);
329
+ char alter_dir_char_for_block = DIR_CHARS[slide_dir_idx];
330
+
331
+ BFSResult path_target_to_slide_start_P = bfs(target_P, slide_start_P, g_after_alter,
332
+ target_P, true, build_action_str);
333
+ if (path_target_to_slide_start_P.cost == INF_COST) return false;
334
+
335
+ res.turns = path_cur_to_target_P.cost + 1 + path_target_to_slide_start_P.cost + 1;
336
+ if (build_action_str) {
337
+ res.actions_str = path_cur_to_target_P.actions_str;
338
+ res.actions_str += 'A'; res.actions_str += alter_dir_char_for_block;
339
+ res.actions_str += path_target_to_slide_start_P.actions_str;
340
+ res.actions_str += 'S'; res.actions_str += slide_dir_char;
341
+ } else {
342
+ res.actions_str.clear();
343
+ }
344
+ g = g_after_alter;
345
+ return true;
346
+ }
347
+ return false;
348
+ }
349
+
350
+ const int NUM_BASE_STRATEGIES_DIRECT = 1;
351
+ const int NUM_BASE_STRATEGIES_UNBLOCK = 1;
352
+ const int NUM_BASE_STRATEGIES_SLIDE_TYPE0 = 4;
353
+ const int NUM_BASE_STRATEGIES_SLIDE_TYPE1 = 4;
354
+ const int NUM_BASE_STRATEGIES = NUM_BASE_STRATEGIES_DIRECT + NUM_BASE_STRATEGIES_UNBLOCK +
355
+ NUM_BASE_STRATEGIES_SLIDE_TYPE0 + NUM_BASE_STRATEGIES_SLIDE_TYPE1; // 1+1+4+4 = 10
356
+
357
+ bool apply_base_strategy_internal(int base_code, Pos cur_P, Pos target_P, Grid& g, SegmentExecResult& res, bool build_action_str) {
358
+ if (base_code == 0) return apply_direct_path_strat(cur_P, target_P, g, res, build_action_str);
359
+ if (base_code == 1) return apply_unblock_and_go_strat(cur_P, target_P, g, res, build_action_str);
360
+
361
+ int type = -1, dir_idx = -1;
362
+ if (base_code >= 2 && base_code < 2 + NUM_BASE_STRATEGIES_SLIDE_TYPE0) {
363
+ type = 0; dir_idx = base_code - 2;
364
+ }
365
+ else if (base_code >= 2 + NUM_BASE_STRATEGIES_SLIDE_TYPE0 &&
366
+ base_code < 2 + NUM_BASE_STRATEGIES_SLIDE_TYPE0 + NUM_BASE_STRATEGIES_SLIDE_TYPE1) {
367
+ type = 1; dir_idx = base_code - (2 + NUM_BASE_STRATEGIES_SLIDE_TYPE0);
368
+ }
369
+ else return false;
370
+
371
+ return apply_slide_strat(cur_P, target_P, g, res, dir_idx, type, build_action_str);
372
+ }
373
+
374
+ const int NUM_POST_ALTER_OPTIONS_NONE = 1;
375
+ const int NUM_POST_ALTER_OPTIONS_ADJACENT = 4;
376
+ const int NUM_POST_ALTER_OPTIONS_MOVE_PLUS_ALTER = 12;
377
+ const int NUM_POST_ALTER_OPTIONS_CUMULATIVE_NONE = NUM_POST_ALTER_OPTIONS_NONE;
378
+ const int NUM_POST_ALTER_OPTIONS_CUMULATIVE_ADJACENT = NUM_POST_ALTER_OPTIONS_CUMULATIVE_NONE + NUM_POST_ALTER_OPTIONS_ADJACENT;
379
+ const int NUM_POST_ALTER_OPTIONS = NUM_POST_ALTER_OPTIONS_CUMULATIVE_ADJACENT + NUM_POST_ALTER_OPTIONS_MOVE_PLUS_ALTER;
380
+ const int TOTAL_STRATEGIES_PER_SEGMENT = NUM_BASE_STRATEGIES * NUM_POST_ALTER_OPTIONS; // 10 * 17 = 170
381
+ const int GREEDY_REOPTIMIZE_SUBSET_SIZE = 40;
382
+
383
+
384
+ bool apply_combined_strategy(int combined_code, Pos& player_pos_ref ,
385
+ Pos segment_target_P, Grid& g ,
386
+ SegmentExecResult& res , bool build_action_str) {
387
+ res.turns = 0;
388
+ res.actions_str.clear();
389
+
390
+ int base_strategy_code = combined_code % NUM_BASE_STRATEGIES;
391
+ int post_alter_option_code = combined_code / NUM_BASE_STRATEGIES;
392
+
393
+ Pos player_original_pos_at_segment_start = player_pos_ref;
394
+ Grid g_original_at_segment_start = g;
395
+
396
+ bool base_success = apply_base_strategy_internal(base_strategy_code, player_original_pos_at_segment_start, segment_target_P, g, res, build_action_str);
397
+
398
+ if (!base_success) {
399
+ g = g_original_at_segment_start;
400
+ return false;
401
+ }
402
+
403
+ Pos player_pos_after_base = segment_target_P;
404
+
405
+ if (post_alter_option_code == 0) {
406
+ // No action
407
+ } else if (post_alter_option_code < NUM_POST_ALTER_OPTIONS_CUMULATIVE_ADJACENT) {
408
+ int alter_dir_idx = post_alter_option_code - NUM_POST_ALTER_OPTIONS_CUMULATIVE_NONE;
409
+ Pos alter_on_P = {player_pos_after_base.r + DR[alter_dir_idx], player_pos_after_base.c + DC[alter_dir_idx]};
410
+
411
+ if (!is_valid_pos(alter_on_P)) {
412
+ g = g_original_at_segment_start;
413
+ return false;
414
+ }
415
+
416
+ res.turns++;
417
+ if (build_action_str) {
418
+ res.actions_str += 'A';
419
+ res.actions_str += DIR_CHARS[alter_dir_idx];
420
+ }
421
+ toggle_block_pos(alter_on_P, g);
422
+ } else {
423
+ int offset_code = post_alter_option_code - NUM_POST_ALTER_OPTIONS_CUMULATIVE_ADJACENT;
424
+ int D1_idx_move = offset_code / 3;
425
+ int D2_choice_idx_alter = offset_code % 3;
426
+
427
+ int D2_idx_alter = -1;
428
+ int current_choice_count = 0;
429
+ for (int d_candidate = 0; d_candidate < 4; ++d_candidate) {
430
+ if (d_candidate == DIR_REV_IDX[D1_idx_move]) continue;
431
+ if (current_choice_count == D2_choice_idx_alter) {
432
+ D2_idx_alter = d_candidate;
433
+ break;
434
+ }
435
+ current_choice_count++;
436
+ }
437
+
438
+ Pos S1_moved_pos = {player_pos_after_base.r + DR[D1_idx_move], player_pos_after_base.c + DC[D1_idx_move]};
439
+ if (!is_valid_pos(S1_moved_pos) || is_blocked_pos(S1_moved_pos, g)) {
440
+ g = g_original_at_segment_start;
441
+ return false;
442
+ }
443
+
444
+ Pos S2_target_of_alter = {S1_moved_pos.r + DR[D2_idx_alter], S1_moved_pos.c + DC[D2_idx_alter]};
445
+ if (!is_valid_pos(S2_target_of_alter)) {
446
+ g = g_original_at_segment_start;
447
+ return false;
448
+ }
449
+
450
+ res.turns += 2;
451
+ if (build_action_str) {
452
+ res.actions_str += 'M'; res.actions_str += DIR_CHARS[D1_idx_move];
453
+ res.actions_str += 'A'; res.actions_str += DIR_CHARS[D2_idx_alter];
454
+ }
455
+ toggle_block_pos(S2_target_of_alter, g);
456
+ player_pos_after_base = S1_moved_pos;
457
+ }
458
+
459
+ player_pos_ref = player_pos_after_base;
460
+ return true;
461
+ }
462
+
463
+ struct PathCacheEntry { Pos player_pos_before_segment; Grid grid_before_segment; int turns_before_segment; };
464
+ struct FullEvalResult { int total_turns; std::string actions_log; bool possible; };
465
+
466
+ FullEvalResult evaluate_choices(const std::vector<int>& choices, Pos initial_P, const std::vector<Pos>& targets,
467
+ bool build_action_str, int k_eval_start_idx,
468
+ const std::vector<PathCacheEntry>* reference_path_cache,
469
+ std::vector<PathCacheEntry>* path_cache_for_new_state) {
470
+ Grid current_grid_sim; Pos player_pos_sim; int total_turns_sim = 0;
471
+ std::string total_actions_log_sim_segments_builder = "";
472
+
473
+ if (k_eval_start_idx == 0 || reference_path_cache == nullptr || reference_path_cache->empty() || (NUM_SEGMENTS > 0 && k_eval_start_idx >= static_cast<int>(reference_path_cache->size())) ) {
474
+ for(int r=0; r<N_GRID; ++r) for(int c=0; c<N_GRID; ++c) current_grid_sim[r][c] = false;
475
+ player_pos_sim = initial_P;
476
+ total_turns_sim = 0;
477
+ if (k_eval_start_idx != 0 && NUM_SEGMENTS > 0) k_eval_start_idx = 0;
478
+ } else {
479
+ const PathCacheEntry& prev_entry = (*reference_path_cache)[k_eval_start_idx];
480
+ current_grid_sim = prev_entry.grid_before_segment;
481
+ player_pos_sim = prev_entry.player_pos_before_segment;
482
+ total_turns_sim = prev_entry.turns_before_segment;
483
+ if (total_turns_sim == INF_COST) {
484
+ return {INF_COST, "", false};
485
+ }
486
+ }
487
+
488
+ if (path_cache_for_new_state != nullptr && k_eval_start_idx > 0 && reference_path_cache != nullptr && !reference_path_cache->empty() &&
489
+ static_cast<int>(path_cache_for_new_state->size()) >= k_eval_start_idx && static_cast<int>(reference_path_cache->size()) >= k_eval_start_idx) {
490
+ std::copy(reference_path_cache->begin(), reference_path_cache->begin() + k_eval_start_idx, path_cache_for_new_state->begin());
491
+ }
492
+
493
+ for (int seg_idx = k_eval_start_idx; seg_idx < NUM_SEGMENTS; ++seg_idx) {
494
+ if (path_cache_for_new_state != nullptr && !path_cache_for_new_state->empty() && static_cast<int>(path_cache_for_new_state->size()) > seg_idx) {
495
+ (*path_cache_for_new_state)[seg_idx].player_pos_before_segment = player_pos_sim;
496
+ (*path_cache_for_new_state)[seg_idx].grid_before_segment = current_grid_sim;
497
+ (*path_cache_for_new_state)[seg_idx].turns_before_segment = total_turns_sim;
498
+ }
499
+
500
+ Pos target_P_for_segment = targets[seg_idx];
501
+ SegmentExecResult segment_res;
502
+
503
+ bool success = apply_combined_strategy(choices[seg_idx], player_pos_sim, target_P_for_segment, current_grid_sim, segment_res, build_action_str);
504
+
505
+ if (!success || segment_res.turns == INF_COST || total_turns_sim + segment_res.turns > MAX_TOTAL_TURNS) {
506
+ if (path_cache_for_new_state != nullptr && !path_cache_for_new_state->empty()) {
507
+ for(int fill_inf_idx = seg_idx; fill_inf_idx < NUM_SEGMENTS; ++fill_inf_idx) {
508
+ if (static_cast<int>(path_cache_for_new_state->size()) > fill_inf_idx)
509
+ (*path_cache_for_new_state)[fill_inf_idx].turns_before_segment = INF_COST;
510
+ }
511
+ }
512
+ return {INF_COST, "", false};
513
+ }
514
+
515
+ if (build_action_str) total_actions_log_sim_segments_builder += segment_res.actions_str;
516
+ total_turns_sim += segment_res.turns;
517
+ }
518
+ return {total_turns_sim, total_actions_log_sim_segments_builder, true};
519
+ }
520
+
521
+ auto time_start = std::chrono::steady_clock::now();
522
+ double get_elapsed_time_ms() { return std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - time_start).count(); }
523
+ const double TIME_LIMIT_MS = 1950.0;
524
+
525
+ enum class NeighborhoodOpType {
526
+ RANDOM_MULTI_SEGMENT,
527
+ FINE_TWEAK_SINGLE_SEGMENT,
528
+ GREEDY_REOPTIMIZE_SINGLE_SEGMENT
529
+ };
530
+
531
+ int main() {
532
+ std::ios_base::sync_with_stdio(false); std::cin.tie(NULL);
533
+
534
+ int N_in_dummy, M_in_dummy; std::cin >> N_in_dummy >> M_in_dummy;
535
+ std::cin >> G_initial_pos.r >> G_initial_pos.c;
536
+
537
+ if (NUM_SEGMENTS > 0) {
538
+ G_targets_vec.resize(NUM_SEGMENTS);
539
+ for (int k=0; k < NUM_SEGMENTS; ++k) std::cin >> G_targets_vec[k].r >> G_targets_vec[k].c;
540
+ }
541
+
542
+ std::vector<int> current_sa_choices(NUM_SEGMENTS > 0 ? NUM_SEGMENTS : 0);
543
+ std::vector<int> best_sa_choices(NUM_SEGMENTS > 0 ? NUM_SEGMENTS : 0);
544
+
545
+ std::vector<PathCacheEntry> current_path_cache(NUM_SEGMENTS > 0 ? NUM_SEGMENTS : 0);
546
+ std::vector<PathCacheEntry> neighbor_path_cache(NUM_SEGMENTS > 0 ? NUM_SEGMENTS : 0);
547
+
548
+ int current_total_turns = INF_COST;
549
+ int best_total_turns = INF_COST;
550
+ std::string best_actions_log_str = "";
551
+ bool best_is_from_sa = false;
552
+ int initial_greedy_score_turns = INF_COST;
553
+
554
+ if (NUM_SEGMENTS == 0) {
555
+ // No actions
556
+ } else {
557
+ Grid greedy_grid_sim_build;
558
+ for(int r=0; r<N_GRID; ++r) for(int c=0; c<N_GRID; ++c) greedy_grid_sim_build[r][c] = false;
559
+ Pos player_pos_sim_build = G_initial_pos;
560
+ std::string greedy_actions_log_build_temp = "";
561
+ int greedy_total_turns_build_temp = 0;
562
+ bool possible_greedy = true;
563
+
564
+ for (int k = 0; k < NUM_SEGMENTS; ++k) {
565
+ current_path_cache[k].player_pos_before_segment = player_pos_sim_build;
566
+ current_path_cache[k].grid_before_segment = greedy_grid_sim_build;
567
+ current_path_cache[k].turns_before_segment = greedy_total_turns_build_temp;
568
+
569
+ Pos target_P_k = G_targets_vec[k];
570
+ int current_best_strategy_code_for_k = -1;
571
+ int current_min_turns_for_segment_k = INF_COST;
572
+
573
+ for (int code = 0; code < TOTAL_STRATEGIES_PER_SEGMENT; ++code) {
574
+ SegmentExecResult temp_segment_res_eval;
575
+ Grid temp_grid_eval = greedy_grid_sim_build;
576
+ Pos temp_player_pos_eval = player_pos_sim_build;
577
+
578
+ bool success = apply_combined_strategy(code, temp_player_pos_eval, target_P_k, temp_grid_eval, temp_segment_res_eval, false);
579
+
580
+ if (success && temp_segment_res_eval.turns < current_min_turns_for_segment_k) {
581
+ current_min_turns_for_segment_k = temp_segment_res_eval.turns;
582
+ current_best_strategy_code_for_k = code;
583
+ }
584
+ }
585
+
586
+ if (current_best_strategy_code_for_k == -1 || greedy_total_turns_build_temp + current_min_turns_for_segment_k > MAX_TOTAL_TURNS) {
587
+ possible_greedy = false; break;
588
+ }
589
+
590
+ current_sa_choices[k] = current_best_strategy_code_for_k;
591
+
592
+ SegmentExecResult final_segment_res_for_k_build;
593
+ apply_combined_strategy(current_best_strategy_code_for_k,
594
+ player_pos_sim_build,
595
+ target_P_k,
596
+ greedy_grid_sim_build,
597
+ final_segment_res_for_k_build,
598
+ true);
599
+
600
+ greedy_actions_log_build_temp += final_segment_res_for_k_build.actions_str;
601
+ greedy_total_turns_build_temp += final_segment_res_for_k_build.turns;
602
+ }
603
+
604
+ if(possible_greedy) {
605
+ current_total_turns = greedy_total_turns_build_temp;
606
+ best_total_turns = greedy_total_turns_build_temp;
607
+ initial_greedy_score_turns = greedy_total_turns_build_temp;
608
+ best_sa_choices = current_sa_choices;
609
+ best_actions_log_str = greedy_actions_log_build_temp;
610
+ } else {
611
+ Grid fallback_grid_sim; for(int r=0; r<N_GRID; ++r) for(int c=0; c<N_GRID; ++c) fallback_grid_sim[r][c] = false;
612
+ Pos fallback_player_pos = G_initial_pos;
613
+ int fallback_total_turns = 0;
614
+
615
+ for(int k_fallback=0; k_fallback<NUM_SEGMENTS; ++k_fallback) {
616
+ current_path_cache[k_fallback].player_pos_before_segment = fallback_player_pos;
617
+ current_path_cache[k_fallback].grid_before_segment = fallback_grid_sim;
618
+ current_path_cache[k_fallback].turns_before_segment = fallback_total_turns;
619
+
620
+ Pos target_P_k_fallback = G_targets_vec[k_fallback];
621
+ int chosen_code_fallback = -1;
622
+ SegmentExecResult res_simple_direct, res_simple_unblock;
623
+
624
+ Grid temp_grid_direct = fallback_grid_sim; Pos temp_pos_direct = fallback_player_pos;
625
+ bool success_direct = apply_combined_strategy(0, temp_pos_direct, target_P_k_fallback, temp_grid_direct, res_simple_direct, false);
626
+
627
+ Grid temp_grid_unblock = fallback_grid_sim; Pos temp_pos_unblock = fallback_player_pos;
628
+ bool success_unblock = apply_combined_strategy(1, temp_pos_unblock, target_P_k_fallback, temp_grid_unblock, res_simple_unblock, false);
629
+
630
+ if (success_direct && (!success_unblock || res_simple_direct.turns <= res_simple_unblock.turns)) {
631
+ chosen_code_fallback = 0;
632
+ } else if (success_unblock) {
633
+ chosen_code_fallback = 1;
634
+ } else {
635
+ chosen_code_fallback = std::uniform_int_distribution<>(0, TOTAL_STRATEGIES_PER_SEGMENT - 1)(rng);
636
+ }
637
+ current_sa_choices[k_fallback] = chosen_code_fallback;
638
+
639
+ SegmentExecResult temp_res_chosen_fallback;
640
+ bool success_chosen_fb = apply_combined_strategy(chosen_code_fallback, fallback_player_pos, target_P_k_fallback, fallback_grid_sim, temp_res_chosen_fallback, false);
641
+ if (!success_chosen_fb || fallback_total_turns + temp_res_chosen_fallback.turns > MAX_TOTAL_TURNS) {
642
+ for(int fill_idx = k_fallback; fill_idx < NUM_SEGMENTS; ++fill_idx) {
643
+ if (static_cast<int>(current_path_cache.size()) > fill_idx)
644
+ current_path_cache[fill_idx].turns_before_segment = INF_COST;
645
+ }
646
+ break;
647
+ }
648
+ fallback_total_turns += temp_res_chosen_fallback.turns;
649
+ }
650
+
651
+ FullEvalResult fallback_eval = evaluate_choices(current_sa_choices, G_initial_pos, G_targets_vec, false, 0, nullptr, &current_path_cache);
652
+ if (fallback_eval.possible) {
653
+ current_total_turns = fallback_eval.total_turns;
654
+ if (current_total_turns < best_total_turns) {
655
+ best_total_turns = current_total_turns;
656
+ best_sa_choices = current_sa_choices;
657
+ best_is_from_sa = true;
658
+ }
659
+ } else { current_total_turns = INF_COST; }
660
+
661
+ if (current_total_turns == INF_COST) {
662
+ for(int k_rand_init=0; k_rand_init<NUM_SEGMENTS; ++k_rand_init) {
663
+ current_sa_choices[k_rand_init] = std::uniform_int_distribution<>(0, TOTAL_STRATEGIES_PER_SEGMENT - 1)(rng);
664
+ }
665
+ FullEvalResult random_init_eval = evaluate_choices(current_sa_choices, G_initial_pos, G_targets_vec, false, 0, nullptr, &current_path_cache);
666
+ if (random_init_eval.possible) {
667
+ current_total_turns = random_init_eval.total_turns;
668
+ if (current_total_turns < best_total_turns) {
669
+ best_total_turns = current_total_turns;
670
+ best_sa_choices = current_sa_choices;
671
+ best_is_from_sa = true;
672
+ }
673
+ }
674
+ }
675
+ }
676
+
677
+ double T_param_start = 20.0, T_param_end = 0.01;
678
+ std::vector<int> segment_indices_for_shuffle(NUM_SEGMENTS);
679
+ if (NUM_SEGMENTS > 0) std::iota(segment_indices_for_shuffle.begin(), segment_indices_for_shuffle.end(), 0);
680
+
681
+ int iterations_stuck_at_inf = 0;
682
+ const int MAX_STUCK_ITERATIONS_FOR_RANDOM_RESTART = 50;
683
+
684
+ while (get_elapsed_time_ms() < TIME_LIMIT_MS) {
685
+ if (current_total_turns == INF_COST) {
686
+ iterations_stuck_at_inf++;
687
+ if (iterations_stuck_at_inf > MAX_STUCK_ITERATIONS_FOR_RANDOM_RESTART) {
688
+ iterations_stuck_at_inf = 0;
689
+ for(int k_rand_init=0; k_rand_init<NUM_SEGMENTS; ++k_rand_init) {
690
+ current_sa_choices[k_rand_init] = std::uniform_int_distribution<>(0, TOTAL_STRATEGIES_PER_SEGMENT - 1)(rng);
691
+ }
692
+ FullEvalResult random_restart_eval = evaluate_choices(current_sa_choices, G_initial_pos, G_targets_vec, false, 0, nullptr, &current_path_cache);
693
+ if (random_restart_eval.possible) {
694
+ current_total_turns = random_restart_eval.total_turns;
695
+ if (current_total_turns < best_total_turns) {
696
+ best_total_turns = current_total_turns;
697
+ best_sa_choices = current_sa_choices;
698
+ best_is_from_sa = true;
699
+ }
700
+ }
701
+ }
702
+ } else {
703
+ iterations_stuck_at_inf = 0;
704
+ }
705
+
706
+ if (NUM_SEGMENTS == 0) break;
707
+
708
+ std::vector<int> neighbor_sa_choices_temp = current_sa_choices;
709
+ int k_eval_start_idx = NUM_SEGMENTS;
710
+ bool changed_anything_in_choices_vector = false;
711
+
712
+ double op_type_roll = std::uniform_real_distribution<>(0.0, 1.0)(rng);
713
+ NeighborhoodOpType current_op_type_local;
714
+
715
+ if (op_type_roll < 0.50) current_op_type_local = NeighborhoodOpType::RANDOM_MULTI_SEGMENT;
716
+ else if (op_type_roll < 0.85) current_op_type_local = NeighborhoodOpType::FINE_TWEAK_SINGLE_SEGMENT;
717
+ else current_op_type_local = NeighborhoodOpType::GREEDY_REOPTIMIZE_SINGLE_SEGMENT;
718
+
719
+ if (current_op_type_local == NeighborhoodOpType::RANDOM_MULTI_SEGMENT) {
720
+ int num_local_changes;
721
+ double r_nc_dist = std::uniform_real_distribution<>(0.0, 1.0)(rng);
722
+ int max_pert_base = std::max(1, NUM_SEGMENTS / 5);
723
+
724
+ if (r_nc_dist < 0.60) num_local_changes = 1;
725
+ else if (r_nc_dist < 0.85) num_local_changes = 2;
726
+ else if (r_nc_dist < 0.95) num_local_changes = 3;
727
+ else num_local_changes = std::min(NUM_SEGMENTS,
728
+ static_cast<int>(4 + std::uniform_int_distribution<>(0, std::max(0, max_pert_base - 4))(rng))
729
+ );
730
+
731
+ num_local_changes = std::min(num_local_changes, NUM_SEGMENTS);
732
+ num_local_changes = std::max(1, num_local_changes);
733
+
734
+ changed_anything_in_choices_vector = true;
735
+ double r_mt_dist = std::uniform_real_distribution<>(0.0, 1.0)(rng);
736
+ if (r_mt_dist < 0.80 || num_local_changes >= NUM_SEGMENTS ) {
737
+ std::shuffle(segment_indices_for_shuffle.begin(), segment_indices_for_shuffle.end(), rng);
738
+ int min_k_changed_val = NUM_SEGMENTS;
739
+ for (int i_change = 0; i_change < num_local_changes; ++i_change) {
740
+ int k_to_change = segment_indices_for_shuffle[i_change];
741
+ min_k_changed_val = std::min(min_k_changed_val, k_to_change);
742
+
743
+ int old_code = neighbor_sa_choices_temp[k_to_change];
744
+ int new_code = old_code;
745
+ if (TOTAL_STRATEGIES_PER_SEGMENT > 1) {
746
+ do { new_code = std::uniform_int_distribution<>(0, TOTAL_STRATEGIES_PER_SEGMENT - 1)(rng); } while (new_code == old_code);
747
+ } else { new_code = 0; }
748
+ neighbor_sa_choices_temp[k_to_change] = new_code;
749
+ }
750
+ k_eval_start_idx = min_k_changed_val;
751
+ } else {
752
+ int L = num_local_changes;
753
+ int k_start_block = std::uniform_int_distribution<>(0, NUM_SEGMENTS - L)(rng);
754
+ for (int i = 0; i < L; ++i) {
755
+ int k_to_change = k_start_block + i;
756
+ int old_code = neighbor_sa_choices_temp[k_to_change];
757
+ int new_code = old_code;
758
+ if (TOTAL_STRATEGIES_PER_SEGMENT > 1) {
759
+ do { new_code = std::uniform_int_distribution<>(0, TOTAL_STRATEGIES_PER_SEGMENT - 1)(rng); } while (new_code == old_code);
760
+ } else { new_code = 0; }
761
+ neighbor_sa_choices_temp[k_to_change] = new_code;
762
+ }
763
+ k_eval_start_idx = k_start_block;
764
+ }
765
+ } else if (current_op_type_local == NeighborhoodOpType::FINE_TWEAK_SINGLE_SEGMENT) {
766
+ changed_anything_in_choices_vector = true;
767
+ int k_to_change = std::uniform_int_distribution<>(0, NUM_SEGMENTS - 1)(rng);
768
+ k_eval_start_idx = k_to_change;
769
+ int current_strategy_code = neighbor_sa_choices_temp[k_to_change];
770
+ int base_code = current_strategy_code % NUM_BASE_STRATEGIES;
771
+ int post_alter_code = current_strategy_code / NUM_BASE_STRATEGIES;
772
+
773
+ double tweak_type_rand = std::uniform_real_distribution<>(0.0, 1.0)(rng);
774
+ if (tweak_type_rand < 0.5 && NUM_POST_ALTER_OPTIONS > 1) {
775
+ int new_post_alter_code = post_alter_code;
776
+ do { new_post_alter_code = std::uniform_int_distribution<>(0, NUM_POST_ALTER_OPTIONS - 1)(rng); } while (new_post_alter_code == post_alter_code);
777
+ neighbor_sa_choices_temp[k_to_change] = new_post_alter_code * NUM_BASE_STRATEGIES + base_code;
778
+ } else if (NUM_BASE_STRATEGIES > 1) {
779
+ int new_base_code = base_code;
780
+ do { new_base_code = std::uniform_int_distribution<>(0, NUM_BASE_STRATEGIES - 1)(rng); } while (new_base_code == base_code);
781
+ neighbor_sa_choices_temp[k_to_change] = post_alter_code * NUM_BASE_STRATEGIES + new_base_code;
782
+ } else {
783
+ if (TOTAL_STRATEGIES_PER_SEGMENT > 1) {
784
+ int new_code = current_strategy_code;
785
+ do { new_code = std::uniform_int_distribution<>(0, TOTAL_STRATEGIES_PER_SEGMENT - 1)(rng); } while (new_code == current_strategy_code);
786
+ neighbor_sa_choices_temp[k_to_change] = new_code;
787
+ } else { changed_anything_in_choices_vector = false; }
788
+ }
789
+ if (neighbor_sa_choices_temp[k_to_change] == current_sa_choices[k_to_change]) {
790
+ changed_anything_in_choices_vector = false;
791
+ }
792
+
793
+ } else { // GREEDY_REOPTIMIZE_SINGLE_SEGMENT
794
+ int k_to_reoptimize = std::uniform_int_distribution<>(0, NUM_SEGMENTS - 1)(rng);
795
+
796
+ if (current_total_turns == INF_COST || current_path_cache.empty() ||
797
+ k_to_reoptimize >= static_cast<int>(current_path_cache.size()) ||
798
+ current_path_cache[k_to_reoptimize].turns_before_segment == INF_COST) {
799
+ changed_anything_in_choices_vector = false;
800
+ } else {
801
+ k_eval_start_idx = k_to_reoptimize;
802
+
803
+ Pos player_pos_before_k = current_path_cache[k_to_reoptimize].player_pos_before_segment;
804
+ Grid grid_before_k = current_path_cache[k_to_reoptimize].grid_before_segment;
805
+ Pos target_P_k = G_targets_vec[k_to_reoptimize];
806
+
807
+ int original_choice_for_k = current_sa_choices[k_to_reoptimize];
808
+ int best_strategy_for_k = original_choice_for_k;
809
+ SegmentExecResult best_res_for_k_eval;
810
+
811
+ Grid temp_grid_eval_current = grid_before_k; Pos temp_player_pos_eval_current = player_pos_before_k;
812
+ bool current_choice_possible = apply_combined_strategy(original_choice_for_k, temp_player_pos_eval_current, target_P_k, temp_grid_eval_current, best_res_for_k_eval, false);
813
+ if (!current_choice_possible) best_res_for_k_eval.turns = INF_COST;
814
+
815
+ for (int i = 0; i < GREEDY_REOPTIMIZE_SUBSET_SIZE; ++i) {
816
+ int code_to_try = std::uniform_int_distribution<>(0, TOTAL_STRATEGIES_PER_SEGMENT - 1)(rng);
817
+ if (code_to_try == original_choice_for_k && current_choice_possible) {
818
+ continue;
819
+ }
820
+
821
+ SegmentExecResult current_segment_res_eval;
822
+ Grid temp_grid_iter_eval = grid_before_k;
823
+ Pos temp_player_pos_iter_eval = player_pos_before_k;
824
+ bool success = apply_combined_strategy(code_to_try, temp_player_pos_iter_eval, target_P_k, temp_grid_iter_eval, current_segment_res_eval, false);
825
+
826
+ if (success && current_segment_res_eval.turns < best_res_for_k_eval.turns) {
827
+ best_res_for_k_eval.turns = current_segment_res_eval.turns;
828
+ best_strategy_for_k = code_to_try;
829
+ }
830
+ }
831
+ neighbor_sa_choices_temp[k_to_reoptimize] = best_strategy_for_k;
832
+ if (best_strategy_for_k != original_choice_for_k) {
833
+ changed_anything_in_choices_vector = true;
834
+ }
835
+ }
836
+ }
837
+
838
+ if (!changed_anything_in_choices_vector) continue;
839
+
840
+ FullEvalResult neighbor_eval_res = evaluate_choices(neighbor_sa_choices_temp, G_initial_pos, G_targets_vec,
841
+ false, k_eval_start_idx,
842
+ &current_path_cache, &neighbor_path_cache);
843
+
844
+ if (neighbor_eval_res.possible) {
845
+ bool accepted = false;
846
+ if (neighbor_eval_res.total_turns < current_total_turns) { accepted = true; }
847
+ else if (current_total_turns != INF_COST) {
848
+ double temperature = T_param_start;
849
+ double progress = get_elapsed_time_ms() / TIME_LIMIT_MS;
850
+ if (progress < 1.0 && progress >=0.0) { temperature = T_param_start * std::pow(T_param_end / T_param_start, progress); }
851
+ else if (progress >= 1.0) { temperature = T_param_end; }
852
+ temperature = std::max(temperature, T_param_end);
853
+ if (temperature > 1e-9) {
854
+ double delta_cost = static_cast<double>(neighbor_eval_res.total_turns - current_total_turns);
855
+ if (std::exp(-delta_cost / temperature) > std::uniform_real_distribution<>(0.0, 1.0)(rng) ) { accepted = true; }
856
+ }
857
+ } else {
858
+ accepted = true;
859
+ }
860
+
861
+ if (accepted) {
862
+ current_sa_choices.swap(neighbor_sa_choices_temp);
863
+ current_total_turns = neighbor_eval_res.total_turns;
864
+ if (!current_path_cache.empty() && !neighbor_path_cache.empty()) {
865
+ current_path_cache.swap(neighbor_path_cache);
866
+ }
867
+
868
+ if (current_total_turns < best_total_turns) {
869
+ best_total_turns = current_total_turns;
870
+ best_sa_choices = current_sa_choices;
871
+ best_is_from_sa = true;
872
+ }
873
+ }
874
+ }
875
+ }
876
+
877
+ if (best_total_turns == INF_COST) {
878
+ best_actions_log_str = "";
879
+ } else {
880
+ if (best_is_from_sa || !possible_greedy || best_total_turns < initial_greedy_score_turns) {
881
+ FullEvalResult final_best_res = evaluate_choices(best_sa_choices, G_initial_pos, G_targets_vec, true, 0, nullptr, nullptr);
882
+ if (final_best_res.possible) {
883
+ best_actions_log_str = final_best_res.actions_log;
884
+ } else {
885
+ best_actions_log_str = "";
886
+ }
887
+ }
888
+ }
889
+ }
890
+
891
+ const std::string& final_actions_to_print = best_actions_log_str;
892
+ for (size_t i = 0; i < final_actions_to_print.length(); i += 2) {
893
+ std::cout << final_actions_to_print[i] << " " << final_actions_to_print[i+1] << "\n";
894
+ }
895
+ return 0;
896
+ }
897
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/ale_agent_best/ahc016.cpp ADDED
@@ -0,0 +1,495 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ #ifndef ONLINE_JUDGE
3
+ // #define DEBUG_OUTPUT // Uncomment for local debug prints
4
+ #endif
5
+
6
+ #include <iostream>
7
+ #include <vector>
8
+ #include <string>
9
+ #include <numeric>
10
+ #include <algorithm>
11
+ #include <random>
12
+ #include <set>
13
+ #include <array>
14
+ #include <iomanip>
15
+ #include <cmath>
16
+ #include <chrono>
17
+ #include <map>
18
+
19
+ // Max N for which we attempt full GED based strategy.
20
+ constexpr int N_MAX_GED_CAP = 6;
21
+
22
+ // Adjacency matrix for H_k received in query, or for G_i during pairwise GED. Max N=100
23
+ bool CURRENT_GRAPH_ADJ_QUERY[100][100];
24
+
25
+ int N_ACTUAL;
26
+ int L_ACTUAL; // N_ACTUAL * (N_ACTUAL - 1) / 2
27
+
28
+ // Stores chosen G_j graphs as adjacency matrices (for GED strategy, N <= N_MAX_GED_CAP)
29
+ std::vector<std::array<std::array<bool, N_MAX_GED_CAP>, N_MAX_GED_CAP>> G_ADJS_CHOSEN_GED;
30
+
31
+ // For large N strategy (edge density)
32
+ std::vector<std::string> G_STRINGS_CHOSEN_LARGE_N;
33
+ std::vector<int> G_EDGE_COUNTS_LARGE_N;
34
+
35
+ std::vector<int> P_VERTS_PERM_QUERY; // Permutation vector for GED in query
36
+ std::mt19937 RND_ENGINE;
37
+
38
+ // Temp storage for canonical mask generation (N <= N_MAX_GED_CAP)
39
+ bool CANON_TMP_ADJ[N_MAX_GED_CAP][N_MAX_GED_CAP];
40
+ std::vector<int> CANON_P_PERM;
41
+
42
+ enum class Strategy {
43
+ GED,
44
+ EDGE_COUNT
45
+ };
46
+ Strategy current_strategy;
47
+
48
+ const std::vector<uint16_t> PRECOMPUTED_CANONICAL_MASKS_N6 = {
49
+ 0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 27, 29, 31, 37, 39, 43, 45, 47, 53, 55, 61,
50
+ 63, 73, 75, 77, 79, 91, 93, 95, 111, 117, 119, 125, 127, 141, 143, 157, 159, 173, 175,
51
+ 181, 183, 189, 191, 205, 207, 221, 223, 237, 239, 253, 255, 285, 287, 315, 317, 319,
52
+ 349, 351, 379, 381, 383, 413, 415, 445, 447, 477, 479, 509, 511, 565, 567, 573, 575,
53
+ 589, 591, 605, 607, 637, 639, 701, 703, 717, 719, 733, 735, 749, 751, 765, 767, 797,
54
+ 799, 829, 831, 861, 863, 893, 895, 957, 959, 989, 991, 1021, 1023, 1149, 1151, 1213,
55
+ 1215, 1245, 1247, 1277, 1279, 1533, 1535, 1661, 1663, 1789, 1791, 1917, 1919, 2045,
56
+ 2047, 2109, 2111, 2141, 2143, 2173, 2175, 2205, 2207, 2237, 2239, 2269, 2271, 2301,
57
+ 2303, 2685, 2687, 2813, 2815, 2941, 2943, 3069, 3071, 3277, 3279, 3285, 3287, 3293,
58
+ 3295, 3309, 3311, 3325, 3327, 3357, 3359, 3389, 3391, 3421, 3423, 3453, 3455, 3517,
59
+ 3519, 3549, 3551, 3581, 3583, 3613, 3615, 3645, 3647, 3709, 3711, 3773, 3775, 3837,
60
+ 3839, 4095, 8191, 16383, 32767
61
+ }; // Total 156 graphs for N=6.
62
+
63
+
64
+ void mask_to_adj_matrix_small_N(uint16_t mask, int N_nodes, bool adj_matrix[][N_MAX_GED_CAP]) {
65
+ int bit_idx = 0;
66
+ for (int i = 0; i < N_nodes; ++i) {
67
+ adj_matrix[i][i] = false;
68
+ for (int j = i + 1; j < N_nodes; ++j) {
69
+ adj_matrix[i][j] = adj_matrix[j][i] = ((mask >> bit_idx) & 1);
70
+ bit_idx++;
71
+ }
72
+ }
73
+ }
74
+
75
+ uint16_t adj_matrix_to_mask_small_N(int N_nodes, const bool adj_matrix[][N_MAX_GED_CAP], const std::vector<int>& p_perm) {
76
+ uint16_t mask = 0;
77
+ int bit_idx = 0;
78
+ for (int i = 0; i < N_nodes; ++i) {
79
+ for (int j = i + 1; j < N_nodes; ++j) {
80
+ if (adj_matrix[p_perm[i]][p_perm[j]]) {
81
+ mask |= (1U << bit_idx);
82
+ }
83
+ bit_idx++;
84
+ }
85
+ }
86
+ return mask;
87
+ }
88
+
89
+ uint16_t get_canonical_mask(uint16_t mask_val) {
90
+ int current_L_for_canon = N_ACTUAL * (N_ACTUAL - 1) / 2;
91
+ if (current_L_for_canon == 0) return 0;
92
+
93
+ mask_to_adj_matrix_small_N(mask_val, N_ACTUAL, CANON_TMP_ADJ);
94
+
95
+ std::iota(CANON_P_PERM.begin(), CANON_P_PERM.end(), 0);
96
+ uint16_t min_mask_representation = adj_matrix_to_mask_small_N(N_ACTUAL, CANON_TMP_ADJ, CANON_P_PERM);
97
+
98
+ while (std::next_permutation(CANON_P_PERM.begin(), CANON_P_PERM.end())) {
99
+ uint16_t current_perm_mask = adj_matrix_to_mask_small_N(N_ACTUAL, CANON_TMP_ADJ, CANON_P_PERM);
100
+ min_mask_representation = std::min(min_mask_representation, current_perm_mask);
101
+ }
102
+ return min_mask_representation;
103
+ }
104
+
105
+ int calculate_edit_distance_one_perm_small_N(
106
+ const std::array<std::array<bool, N_MAX_GED_CAP>, N_MAX_GED_CAP>& g_j_adj_template
107
+ ) {
108
+ int diff_count = 0;
109
+ for (int i = 0; i < N_ACTUAL; ++i) {
110
+ for (int j = i + 1; j < N_ACTUAL; ++j) {
111
+ bool template_has_edge = g_j_adj_template[i][j];
112
+ bool current_Hk_has_edge = CURRENT_GRAPH_ADJ_QUERY[P_VERTS_PERM_QUERY[i]][P_VERTS_PERM_QUERY[j]];
113
+ if (current_Hk_has_edge != template_has_edge) {
114
+ diff_count++;
115
+ }
116
+ }
117
+ }
118
+ return diff_count;
119
+ }
120
+
121
+ int min_edit_distance_global_perm_small_N(
122
+ const std::array<std::array<bool, N_MAX_GED_CAP>, N_MAX_GED_CAP>& g_j_adj_template
123
+ ) {
124
+ if (L_ACTUAL == 0) return 0;
125
+
126
+ std::iota(P_VERTS_PERM_QUERY.begin(), P_VERTS_PERM_QUERY.end(), 0);
127
+ int min_dist = L_ACTUAL + 1;
128
+
129
+ long long N_factorial = 1;
130
+ for(int i=1; i<=N_ACTUAL; ++i) N_factorial *= i;
131
+
132
+ long long ops_count = 0;
133
+ do {
134
+ int current_dist = calculate_edit_distance_one_perm_small_N(g_j_adj_template);
135
+ min_dist = std::min(min_dist, current_dist);
136
+ if (min_dist == 0) break;
137
+
138
+ ops_count++;
139
+ if (ops_count >= N_factorial) break;
140
+ } while (std::next_permutation(P_VERTS_PERM_QUERY.begin(), P_VERTS_PERM_QUERY.end()));
141
+
142
+ return min_dist;
143
+ }
144
+
145
+
146
+ std::vector<uint16_t> available_canonical_masks;
147
+ std::vector<std::vector<int>> all_pairwise_ged_cache;
148
+ std::map<uint16_t, int> mask_to_idx_map;
149
+ std::vector<int> chosen_mask_indices_greedy;
150
+
151
+ std::string generate_random_graph_string_large_n(int num_edges, int current_L) {
152
+ std::string s_out(current_L, '0');
153
+ if (num_edges <= 0 || current_L == 0) return s_out;
154
+ if (num_edges >= current_L) {
155
+ std::fill(s_out.begin(), s_out.end(), '1');
156
+ return s_out;
157
+ }
158
+ std::vector<int> edge_indices(current_L);
159
+ std::iota(edge_indices.begin(), edge_indices.end(), 0);
160
+ std::shuffle(edge_indices.begin(), edge_indices.end(), RND_ENGINE);
161
+ for (int i = 0; i < num_edges; ++i) {
162
+ s_out[edge_indices[i]] = '1';
163
+ }
164
+ return s_out;
165
+ }
166
+
167
+ int count_set_bits_in_string(const std::string& s) {
168
+ return std::count(s.begin(), s.end(), '1');
169
+ }
170
+
171
+ void string_to_adj_matrix_query(const std::string& s, int N_nodes) {
172
+ int char_idx = 0;
173
+ for(int i=0; i<N_nodes; ++i) {
174
+ CURRENT_GRAPH_ADJ_QUERY[i][i] = false;
175
+ for(int j=i+1; j<N_nodes; ++j) {
176
+ if (char_idx < (int)s.length()) {
177
+ CURRENT_GRAPH_ADJ_QUERY[i][j] = CURRENT_GRAPH_ADJ_QUERY[j][i] = (s[char_idx++] == '1');
178
+ } else {
179
+ CURRENT_GRAPH_ADJ_QUERY[i][j] = CURRENT_GRAPH_ADJ_QUERY[j][i] = false;
180
+ }
181
+ }
182
+ }
183
+ }
184
+
185
+
186
+ int main() {
187
+ std::ios_base::sync_with_stdio(false);
188
+ std::cin.tie(NULL);
189
+
190
+ unsigned int seed_val = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
191
+ RND_ENGINE.seed(seed_val);
192
+
193
+ int M_graphs;
194
+ double epsilon_noise_rate;
195
+ std::cin >> M_graphs >> epsilon_noise_rate;
196
+
197
+ int N_for_GED_strat;
198
+ if (M_graphs <= 11) N_for_GED_strat = 4;
199
+ else if (M_graphs <= 34) N_for_GED_strat = 5;
200
+ else N_for_GED_strat = N_MAX_GED_CAP;
201
+
202
+ const double K_SEP = 2.5;
203
+ double L_ideal;
204
+ double L_ideal_numerator = K_SEP * K_SEP * (M_graphs > 1 ? (M_graphs - 1.0) * (M_graphs - 1.0) : 1.0) *
205
+ epsilon_noise_rate * (1.0 - epsilon_noise_rate);
206
+ double L_ideal_denominator_factor = (0.5 - epsilon_noise_rate);
207
+ double L_ideal_denominator = L_ideal_denominator_factor * L_ideal_denominator_factor;
208
+
209
+ if (std::abs(0.5 - epsilon_noise_rate) < 1e-9) {
210
+ L_ideal = (100.0 * 99.0) / 2.0;
211
+ } else {
212
+ L_ideal = L_ideal_numerator / L_ideal_denominator;
213
+ }
214
+ if (L_ideal < 0) L_ideal = 0;
215
+
216
+ int N_candidate_EC = 4;
217
+ if (L_ideal > 1e-9) {
218
+ double discriminant = 1.0 + 8.0 * L_ideal;
219
+ if (discriminant >=0) {
220
+ N_candidate_EC = static_cast<int>(std::ceil((1.0 + std::sqrt(discriminant)) / 2.0));
221
+ } else {
222
+ N_candidate_EC = 100;
223
+ }
224
+ }
225
+ N_candidate_EC = std::max(4, N_candidate_EC);
226
+ N_candidate_EC = std::min(100, N_candidate_EC);
227
+
228
+ if (epsilon_noise_rate < 0.01) {
229
+ current_strategy = Strategy::GED; N_ACTUAL = N_for_GED_strat;
230
+ } else {
231
+ if (N_candidate_EC > N_for_GED_strat) {
232
+ current_strategy = Strategy::EDGE_COUNT; N_ACTUAL = N_candidate_EC;
233
+ } else {
234
+ current_strategy = Strategy::GED; N_ACTUAL = N_for_GED_strat;
235
+ }
236
+ }
237
+ N_ACTUAL = std::min(100, std::max(4, N_ACTUAL)); // Final check on N_ACTUAL bounds
238
+
239
+ L_ACTUAL = N_ACTUAL * (N_ACTUAL - 1) / 2;
240
+ std::cout << N_ACTUAL << std::endl;
241
+
242
+ #ifdef DEBUG_OUTPUT
243
+ std::cerr << "# M=" << M_graphs << ", eps=" << epsilon_noise_rate << std::endl;
244
+ std::cerr << "# Chosen N=" << N_ACTUAL << ", Strategy=" << (current_strategy == Strategy::GED ? "GED" : "EDGE_COUNT") << std::endl;
245
+ std::cerr << "# L_ideal=" << L_ideal << ", N_candidate_EC=" << N_candidate_EC << ", N_for_GED_strat=" << N_for_GED_strat << std::endl;
246
+ #endif
247
+
248
+ if (current_strategy == Strategy::GED) {
249
+ P_VERTS_PERM_QUERY.resize(N_ACTUAL); CANON_P_PERM.resize(N_ACTUAL);
250
+
251
+ if (N_ACTUAL == 6) {
252
+ available_canonical_masks = PRECOMPUTED_CANONICAL_MASKS_N6;
253
+ } else {
254
+ std::set<uint16_t> unique_masks_set;
255
+ if (L_ACTUAL > 0) {
256
+ for (unsigned int i = 0; i < (1U << L_ACTUAL); ++i) {
257
+ unique_masks_set.insert(get_canonical_mask(static_cast<uint16_t>(i)));
258
+ }
259
+ } else {
260
+ unique_masks_set.insert(0);
261
+ }
262
+ available_canonical_masks.assign(unique_masks_set.begin(), unique_masks_set.end());
263
+ }
264
+
265
+ int num_total_isos = available_canonical_masks.size();
266
+ #ifdef DEBUG_OUTPUT
267
+ std::cerr << "# Num non-isomorphic graphs for N=" << N_ACTUAL << " is " << num_total_isos << std::endl;
268
+ #endif
269
+ mask_to_idx_map.clear();
270
+ for(int i=0; i<num_total_isos; ++i) mask_to_idx_map[available_canonical_masks[i]] = i;
271
+
272
+ if (num_total_isos > 0) {
273
+ all_pairwise_ged_cache.assign(num_total_isos, std::vector<int>(num_total_isos, 0));
274
+ bool graph_i_adj_cstyle[N_MAX_GED_CAP][N_MAX_GED_CAP];
275
+ std::array<std::array<bool, N_MAX_GED_CAP>, N_MAX_GED_CAP> graph_j_adj_stdarray;
276
+
277
+ for (int i = 0; i < num_total_isos; ++i) {
278
+ mask_to_adj_matrix_small_N(available_canonical_masks[i], N_ACTUAL, graph_i_adj_cstyle);
279
+ for(int r=0; r<N_ACTUAL; ++r) for(int c=0; c<N_ACTUAL; ++c) CURRENT_GRAPH_ADJ_QUERY[r][c] = graph_i_adj_cstyle[r][c];
280
+
281
+ for (int j = i + 1; j < num_total_isos; ++j) {
282
+ bool temp_adj_for_gj[N_MAX_GED_CAP][N_MAX_GED_CAP];
283
+ mask_to_adj_matrix_small_N(available_canonical_masks[j], N_ACTUAL, temp_adj_for_gj);
284
+ for(int r=0; r<N_ACTUAL; ++r) for(int c=0; c<N_ACTUAL; ++c) graph_j_adj_stdarray[r][c] = temp_adj_for_gj[r][c];
285
+
286
+ all_pairwise_ged_cache[i][j] = all_pairwise_ged_cache[j][i] = min_edit_distance_global_perm_small_N(graph_j_adj_stdarray);
287
+ }
288
+ }
289
+ }
290
+
291
+ chosen_mask_indices_greedy.clear();
292
+ std::vector<bool> is_chosen_idx(num_total_isos, false);
293
+
294
+ if (num_total_isos > 0) {
295
+ if (mask_to_idx_map.count(0)) {
296
+ int zero_idx = mask_to_idx_map.at(0);
297
+ if (chosen_mask_indices_greedy.size() < (size_t)M_graphs) {
298
+ chosen_mask_indices_greedy.push_back(zero_idx);
299
+ is_chosen_idx[zero_idx] = true;
300
+ }
301
+ }
302
+ if (L_ACTUAL > 0 && chosen_mask_indices_greedy.size() < (size_t)M_graphs) {
303
+ uint16_t complete_mask_val = (1U << L_ACTUAL) - 1;
304
+ uint16_t canonical_complete_mask = get_canonical_mask(complete_mask_val);
305
+ if (mask_to_idx_map.count(canonical_complete_mask)) {
306
+ int complete_idx = mask_to_idx_map.at(canonical_complete_mask);
307
+ if (!is_chosen_idx[complete_idx]) {
308
+ chosen_mask_indices_greedy.push_back(complete_idx);
309
+ is_chosen_idx[complete_idx] = true;
310
+ }
311
+ }
312
+ }
313
+ }
314
+
315
+ for (int k_count = chosen_mask_indices_greedy.size(); k_count < M_graphs; ++k_count) {
316
+ if (chosen_mask_indices_greedy.size() >= (size_t)num_total_isos) {
317
+ break;
318
+ }
319
+
320
+ int best_new_idx_to_add = -1;
321
+ int max_of_min_distances_found = -1;
322
+
323
+ for (int cand_idx = 0; cand_idx < num_total_isos; ++cand_idx) {
324
+ if (is_chosen_idx[cand_idx]) continue;
325
+
326
+ int current_cand_min_dist_to_existing_G;
327
+ if (chosen_mask_indices_greedy.empty()) {
328
+ current_cand_min_dist_to_existing_G = L_ACTUAL + 1;
329
+ } else {
330
+ current_cand_min_dist_to_existing_G = L_ACTUAL + 1;
331
+ for (int chosen_idx : chosen_mask_indices_greedy) {
332
+ current_cand_min_dist_to_existing_G = std::min(current_cand_min_dist_to_existing_G, all_pairwise_ged_cache[cand_idx][chosen_idx]);
333
+ }
334
+ }
335
+
336
+ if (current_cand_min_dist_to_existing_G > max_of_min_distances_found) {
337
+ max_of_min_distances_found = current_cand_min_dist_to_existing_G;
338
+ best_new_idx_to_add = cand_idx;
339
+ }
340
+ }
341
+
342
+ if (best_new_idx_to_add != -1) {
343
+ chosen_mask_indices_greedy.push_back(best_new_idx_to_add);
344
+ is_chosen_idx[best_new_idx_to_add] = true;
345
+ } else {
346
+ break;
347
+ }
348
+ }
349
+
350
+ int num_distinct_chosen_graphs = chosen_mask_indices_greedy.size();
351
+ if (num_distinct_chosen_graphs < M_graphs) {
352
+ int fallback_idx = 0;
353
+ if (num_total_isos > 0) {
354
+ if (mask_to_idx_map.count(0)) {
355
+ fallback_idx = mask_to_idx_map.at(0);
356
+ }
357
+ }
358
+
359
+ for (int k_idx = num_distinct_chosen_graphs; k_idx < M_graphs; ++k_idx) {
360
+ if (num_total_isos > 0) {
361
+ chosen_mask_indices_greedy.push_back(fallback_idx);
362
+ } else {
363
+ chosen_mask_indices_greedy.push_back(0);
364
+ }
365
+ }
366
+ }
367
+ #ifdef DEBUG_OUTPUT
368
+ std::cerr << "# Chosen mask indices (size " << chosen_mask_indices_greedy.size() << "): ";
369
+ if (!available_canonical_masks.empty()){ // Check before accessing
370
+ for(int idx : chosen_mask_indices_greedy) {
371
+ if (idx < available_canonical_masks.size()) std::cerr << idx << " (" << available_canonical_masks[idx] << ") ";
372
+ else std::cerr << idx << " (OOB) ";
373
+ }
374
+ }
375
+ std::cerr << std::endl;
376
+ #endif
377
+
378
+ G_ADJS_CHOSEN_GED.resize(M_graphs);
379
+ for (int k_idx = 0; k_idx < M_graphs; ++k_idx) {
380
+ uint16_t mask_to_print = 0;
381
+ if (k_idx < chosen_mask_indices_greedy.size() &&
382
+ !available_canonical_masks.empty() &&
383
+ chosen_mask_indices_greedy[k_idx] < available_canonical_masks.size()) {
384
+ mask_to_print = available_canonical_masks[chosen_mask_indices_greedy[k_idx]];
385
+ } else if (L_ACTUAL == 0 && k_idx < chosen_mask_indices_greedy.size()) {
386
+ mask_to_print = 0;
387
+ }
388
+
389
+ bool temp_adj_cstyle[N_MAX_GED_CAP][N_MAX_GED_CAP];
390
+ mask_to_adj_matrix_small_N(mask_to_print, N_ACTUAL, temp_adj_cstyle);
391
+ for(int r=0; r<N_ACTUAL; ++r) for(int c=0; c<N_ACTUAL; ++c) G_ADJS_CHOSEN_GED[k_idx][r][c] = temp_adj_cstyle[r][c];
392
+
393
+ std::string s_out = "";
394
+ if (L_ACTUAL > 0) {
395
+ for (int bit_idx = 0; bit_idx < L_ACTUAL; ++bit_idx) {
396
+ s_out += ((mask_to_print >> bit_idx) & 1) ? '1' : '0';
397
+ }
398
+ }
399
+ std::cout << s_out << std::endl;
400
+ }
401
+
402
+ } else {
403
+ G_EDGE_COUNTS_LARGE_N.resize(M_graphs); G_STRINGS_CHOSEN_LARGE_N.resize(M_graphs);
404
+ if (M_graphs == 1) {
405
+ G_EDGE_COUNTS_LARGE_N[0] = (L_ACTUAL > 0) ? L_ACTUAL / 2 : 0;
406
+ } else {
407
+ for (int k=0; k<M_graphs; ++k) G_EDGE_COUNTS_LARGE_N[k] = static_cast<int>(std::round((double)k * L_ACTUAL / (M_graphs - 1.0)));
408
+
409
+ for (int k=0; k<M_graphs-1; ++k) {
410
+ if (G_EDGE_COUNTS_LARGE_N[k+1] <= G_EDGE_COUNTS_LARGE_N[k]) {
411
+ G_EDGE_COUNTS_LARGE_N[k+1] = G_EDGE_COUNTS_LARGE_N[k] + 1;
412
+ }
413
+ }
414
+ if (M_graphs > 0 && G_EDGE_COUNTS_LARGE_N[M_graphs-1] > L_ACTUAL) { // M_graphs > 0 check
415
+ int exceso = G_EDGE_COUNTS_LARGE_N[M_graphs-1] - L_ACTUAL;
416
+ for (int k=0; k<M_graphs; ++k) {
417
+ G_EDGE_COUNTS_LARGE_N[k] -= exceso;
418
+ }
419
+ }
420
+ for (int k=0; k<M_graphs; ++k) G_EDGE_COUNTS_LARGE_N[k] = std::min(L_ACTUAL, std::max(0, G_EDGE_COUNTS_LARGE_N[k]));
421
+
422
+ for (int k=0; k<M_graphs-1; ++k) {
423
+ G_EDGE_COUNTS_LARGE_N[k+1] = std::max(G_EDGE_COUNTS_LARGE_N[k+1], G_EDGE_COUNTS_LARGE_N[k] + 1);
424
+ }
425
+ for (int k=0; k<M_graphs; ++k) G_EDGE_COUNTS_LARGE_N[k] = std::min(L_ACTUAL, std::max(0, G_EDGE_COUNTS_LARGE_N[k]));
426
+ }
427
+
428
+ for (int k=0; k<M_graphs; ++k) {
429
+ G_STRINGS_CHOSEN_LARGE_N[k] = generate_random_graph_string_large_n(G_EDGE_COUNTS_LARGE_N[k], L_ACTUAL);
430
+ std::cout << G_STRINGS_CHOSEN_LARGE_N[k] << std::endl;
431
+ }
432
+ }
433
+ std::cout.flush(); // Explicit flush after all G_k are printed
434
+
435
+ for (int q_idx = 0; q_idx < 100; ++q_idx) {
436
+ std::string h_str; std::cin >> h_str;
437
+ if (current_strategy == Strategy::GED) {
438
+ if (M_graphs == 0) { std::cout << 0 << std::endl; std::cout.flush(); continue; }
439
+ if (G_ADJS_CHOSEN_GED.empty()){
440
+ #ifdef DEBUG_OUTPUT
441
+ std::cerr << "# Query " << q_idx << ": G_ADJS_CHOSEN_GED is empty but M_graphs=" << M_graphs << ". Outputting 0." << std::endl;
442
+ #endif
443
+ std::cout << 0 << std::endl; std::cout.flush(); continue;
444
+ }
445
+
446
+ string_to_adj_matrix_query(h_str, N_ACTUAL);
447
+
448
+ int best_g_idx = 0; int min_dist_found = L_ACTUAL + 2;
449
+ for (int j=0; j < M_graphs; ++j) {
450
+ if (j >= G_ADJS_CHOSEN_GED.size()) {
451
+ #ifdef DEBUG_OUTPUT
452
+ std::cerr << "# Query " << q_idx << ": Index j=" << j << " out of bounds for G_ADJS_CHOSEN_GED (size " << G_ADJS_CHOSEN_GED.size() << ")" << std::endl;
453
+ #endif
454
+ continue;
455
+ }
456
+ int dist = min_edit_distance_global_perm_small_N(G_ADJS_CHOSEN_GED[j]);
457
+ if (dist < min_dist_found) {
458
+ min_dist_found = dist;
459
+ best_g_idx = j;
460
+ }
461
+ }
462
+ std::cout << best_g_idx << std::endl;
463
+
464
+ } else {
465
+ if (M_graphs == 0) { std::cout << 0 << std::endl; std::cout.flush(); continue; }
466
+ if (G_EDGE_COUNTS_LARGE_N.empty()){
467
+ #ifdef DEBUG_OUTPUT
468
+ std::cerr << "# Query " << q_idx << ": G_EDGE_COUNTS_LARGE_N is empty but M_graphs=" << M_graphs << ". Outputting 0." << std::endl;
469
+ #endif
470
+ std::cout << 0 << std::endl; std::cout.flush(); continue;
471
+ }
472
+
473
+ int edges_Hk = count_set_bits_in_string(h_str);
474
+ int best_g_idx = 0; double min_abs_diff_expected_edges = -1.0;
475
+ for (int j=0; j<M_graphs; ++j) {
476
+ if (j >= G_EDGE_COUNTS_LARGE_N.size()) {
477
+ #ifdef DEBUG_OUTPUT
478
+ std::cerr << "# Query " << q_idx << ": Index j=" << j << " out of bounds for G_EDGE_COUNTS_LARGE_N (size " << G_EDGE_COUNTS_LARGE_N.size() << ")" << std::endl;
479
+ #endif
480
+ continue;
481
+ }
482
+ double expected_edges_Hk_from_Gj = (double)G_EDGE_COUNTS_LARGE_N[j] * (1.0 - 2.0*epsilon_noise_rate) + (double)L_ACTUAL * epsilon_noise_rate;
483
+ double diff = std::abs((double)edges_Hk - expected_edges_Hk_from_Gj);
484
+ if (min_abs_diff_expected_edges < -0.5 || diff < min_abs_diff_expected_edges) {
485
+ min_abs_diff_expected_edges = diff;
486
+ best_g_idx = j;
487
+ }
488
+ }
489
+ std::cout << best_g_idx << std::endl;
490
+ }
491
+ std::cout.flush(); // Explicit flush after each query prediction
492
+ }
493
+ return 0;
494
+ }
495
+ # EVOLVE-BLOCK-END
benchmarks/ale_bench/private_eval.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import os
4
+ import traceback
5
+ from pathlib import Path
6
+
7
+ import ale_bench
8
+ from ale_bench.result import CaseResult, JudgeResult, Result
9
+
10
+
11
+ def result_feedback(result: Result) -> CaseResult:
12
+ if result.overall_judge_result == JudgeResult.ACCEPTED:
13
+ return result.case_results[0]
14
+ else:
15
+ selected_case_idx = 0
16
+ for idx, case_result in enumerate(result.case_results):
17
+ if case_result.judge_result == result.overall_judge_result:
18
+ selected_case_idx = idx
19
+ break
20
+ return result.case_results[selected_case_idx]
21
+
22
+
23
+ def main(program_path: str, problem_id: str) -> dict:
24
+ """Runs the evaluation using the shinka.eval utility."""
25
+ print(f"Problem ID: {problem_id}")
26
+ print(f"Evaluating program: {program_path}")
27
+
28
+ try:
29
+
30
+ session = ale_bench.start(
31
+ problem_id=problem_id,
32
+ lite_version=False,
33
+ num_workers=13,
34
+ )
35
+
36
+ code = Path(program_path).read_text().replace("# EVOLVE-BLOCK-START", "").replace("# EVOLVE-BLOCK-END", "").strip()
37
+
38
+ private_result, final_rank, final_performance = session.private_eval(
39
+ code, code_language="cpp20",
40
+ )
41
+ # Store the private_result as JSON in the results directory
42
+
43
+ private_json_str = private_result.model_dump_json(indent=4)
44
+ private_json = json.loads(private_json_str)
45
+
46
+ private_passed_cases, private_failed_cases = 0, 0
47
+ num_private_cases = len(private_json["case_results"])
48
+ for case in private_json["case_results"]:
49
+ if case["judge_result"] == "ACCEPTED":
50
+ private_passed_cases += 1
51
+ else:
52
+ private_failed_cases += 1
53
+ print(
54
+ f"Passed {private_passed_cases} cases, failed {private_failed_cases} cases out of {num_private_cases}"
55
+ )
56
+
57
+ print(
58
+ f"Final Private Score: {private_result.overall_absolute_score} - Mean Score: {private_result.overall_absolute_score / num_private_cases}"
59
+ )
60
+ print(f"Rank: {final_rank}, Performance: {final_performance}")
61
+ metrics = {}
62
+ private_metrics = {
63
+ "private_rank": final_rank,
64
+ "private_performance": final_performance,
65
+ "private_score": private_result.overall_absolute_score,
66
+ "num_private_passed_cases": private_passed_cases,
67
+ "num_private_failed_cases": private_failed_cases,
68
+ }
69
+ metrics["private"] = private_metrics
70
+
71
+ # Monitor resource consumption
72
+ print(f"Current Resource Usage: {session.current_resource_usage}")
73
+ print(f"Remaining Resources: {session.remaining_resource_usage}")
74
+
75
+ return metrics
76
+ except Exception as e:
77
+ print(f"Evaluation failed completely: {str(e)}")
78
+ print(traceback.format_exc())
79
+ metrics = {
80
+ "combined_score": 0.0,
81
+ "public": {"judge_result": "REJECTED"},
82
+ "private": {
83
+ "private_rank": 0,
84
+ "private_performance": 0,
85
+ "private_score": 0,
86
+ "num_private_passed_cases": 0,
87
+ "num_private_failed_cases": 0,
88
+ },
89
+ }
90
+ return metrics
91
+
92
+
93
+ if __name__ == "__main__":
94
+ parser = argparse.ArgumentParser(
95
+ description="Agent evaluation script using shinka.eval"
96
+ )
97
+ parser.add_argument(
98
+ "--program-path",
99
+ type=str,
100
+ default="program.cpp",
101
+ help="Path to the program to evaluate",
102
+ )
103
+
104
+ parser.add_argument(
105
+ "--problem-id",
106
+ type=str,
107
+ default="ahc025",
108
+ help="Problem ID",
109
+ )
110
+ parsed_args = parser.parse_args()
111
+
112
+ # Collect results from 3 runs
113
+ all_results = []
114
+ for i in range(3):
115
+ print(f"\n{'='*60}")
116
+ print(f"Running evaluation {i+1} of 3")
117
+ print('='*60)
118
+ result = main(
119
+ parsed_args.program_path,
120
+ parsed_args.problem_id,
121
+ )
122
+ all_results.append(result)
123
+ print('='*60)
124
+
125
+ # Compute averages
126
+ print(f"\n{'='*60}")
127
+ print("FINAL AVERAGED RESULTS ACROSS 3 RUNS")
128
+ print('='*60)
129
+
130
+ private_scores = [r["private"]["private_score"] for r in all_results]
131
+ private_performances = [r["private"]["private_performance"] for r in all_results]
132
+ private_ranks = [r["private"]["private_rank"] for r in all_results]
133
+ passed_cases = [r["private"]["num_private_passed_cases"] for r in all_results]
134
+ failed_cases = [r["private"]["num_private_failed_cases"] for r in all_results]
135
+
136
+ avg_private_score = sum(private_scores) / len(private_scores)
137
+ avg_private_performance = sum(private_performances) / len(private_performances)
138
+ avg_private_rank = sum(private_ranks) / len(private_ranks)
139
+ avg_passed_cases = sum(passed_cases) / len(passed_cases)
140
+ avg_failed_cases = sum(failed_cases) / len(failed_cases)
141
+
142
+ print(f"\nAverage Private Score: {avg_private_score:.2f}")
143
+ print(f" Individual scores: {private_scores}")
144
+ print(f"\nAverage Private Performance: {avg_private_performance:.4f}")
145
+ print(f" Individual performances: {private_performances}")
146
+ print(f"\nAverage Private Rank: {avg_private_rank:.2f}")
147
+ print(f" Individual ranks: {private_ranks}")
148
+ print(f"\nAverage Passed Cases: {avg_passed_cases:.2f}")
149
+ print(f"Average Failed Cases: {avg_failed_cases:.2f}")
150
+ print('='*60)
151
+
152
+ # Return summary
153
+ summary = {
154
+ "avg_private_score": avg_private_score,
155
+ "avg_private_performance": avg_private_performance,
156
+ "avg_private_rank": avg_private_rank,
157
+ "all_results": all_results
158
+ }
159
+
160
+ print(f"\nFinal Summary:")
161
+ print(json.dumps(summary, indent=2))
benchmarks/arc_benchmark/README.md ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ARC Benchmark
2
+
3
+ Evolves ARC-AGI visual reasoning task solutions using SkyDiscover.
4
+
5
+ ## Setup
6
+
7
+ ### 1. Download ARC data
8
+
9
+ Clone the ARC-AGI-2 repo and convert the data:
10
+
11
+ ```bash
12
+ cd benchmarks/arc_benchmark
13
+ git clone https://github.com/arcprize/ARC-AGI-2.git /tmp/ARC-AGI-2
14
+ OUT_DIR=./data uv run python convert_arc_agi2_data.py /tmp/ARC-AGI-2
15
+ rm -rf /tmp/ARC-AGI-2
16
+ ```
17
+
18
+ This creates 4 files in `data/`:
19
+ - `arc-agi_training_challenges.json` (1000 tasks)
20
+ - `arc-agi_training_solutions.json`
21
+ - `arc-agi_evaluation_challenges.json` (120 tasks)
22
+ - `arc-agi_evaluation_solutions.json`
23
+
24
+ ### 2. Set your API key
25
+
26
+ ```bash
27
+ export OPENAI_API_KEY=...
28
+ ```
29
+
30
+ ## Run a single task
31
+
32
+ ARC requires a per-task config (each task has unique training examples as the prompt). Use `generate_config.py` to create one, then run with any search backend:
33
+
34
+ ```bash
35
+ cd benchmarks/arc_benchmark
36
+
37
+ # Generate task-specific config
38
+ TASK_NUM=0 ARC_TASK_FILE=training CONFIG_OUT=./config_task_0.yaml \
39
+ uv run python generate_config.py
40
+
41
+ # Run with any backend
42
+ uv run skydiscover-run initial_program.py evaluator.py \
43
+ -c config_task_0.yaml -s [your_algorithm] -i 30
44
+
45
+ # Or with evox, openevolve, gepa:
46
+ uv run skydiscover-run initial_program.py evaluator.py \
47
+ -c config_task_0.yaml -s [your_algorithm] -i 30
48
+ ```
49
+
50
+ ## Run all evaluation tasks
51
+
52
+ ```bash
53
+ cd benchmarks/arc_benchmark
54
+ export ARC_TASK_FILE=evaluation
55
+
56
+ NUM_TASKS=$(uv run python -c "import json; print(len(json.load(open('data/arc-agi_evaluation_challenges.json'))))")
57
+
58
+ for i in $(seq 0 $((NUM_TASKS - 1))); do
59
+ TASK_NUM=$i CONFIG_OUT=./config_task_${i}.yaml uv run python generate_config.py
60
+ TASK_NUM=$i uv run skydiscover-run initial_program.py evaluator.py \
61
+ -c config_task_${i}.yaml -s [your_algorithm] -i 30 \
62
+ -o outputs/eval_task_${i}
63
+ done
64
+ ```
65
+
66
+ ## Post-discovery test evaluation
67
+
68
+ After the discovery process, evaluate the best program on held-out test inputs:
69
+
70
+ ```bash
71
+ TASK_NUM=0 ARC_TASK_FILE=evaluation \
72
+ OUTS_DIR=./outputs/eval_task_0/adaevolve \
73
+ uv run python post_discovery_eval.py
74
+ ```
75
+
76
+ ## Config: GPT vs Gemini
77
+
78
+ Edit `config.yaml` — comment the GPT block and uncomment the Gemini block, or override with `--model`:
79
+
80
+ ```bash
81
+ uv run skydiscover-run ... -m gemini/gemini-3-pro-preview
82
+ ```
83
+
84
+ ## Files
85
+
86
+ | File | Description |
87
+ |------|-------------|
88
+ | `initial_program.py` | Seed program with two transform functions to evolve |
89
+ | `evaluator.py` | Scores programs on pass@2 + cell accuracy |
90
+ | `config.yaml` | Base config template (prompt injected by generate_config.py) |
91
+ | `generate_config.py` | Injects task-specific training examples into config as system prompt |
92
+ | `post_discovery_eval.py` | Evaluates best program on held-out test inputs |
93
+ | `convert_arc_agi2_data.py` | Converts raw ARC-AGI-2 data to benchmark format |
94
+ | `requirements.txt` | Dependencies (numpy) |
95
+
96
+ ## Environment variables
97
+
98
+ | Variable | Default | Description |
99
+ |----------|---------|-------------|
100
+ | `OPENAI_API_KEY` | (required) | API key |
101
+ | `ARC_TASK_FILE` | `training` | `training` or `evaluation` |
102
+ | `TASK_NUM` | `0` | Task index within the dataset |
103
+ | `BASE_CONFIG` | `./config.yaml` | Base config template path |
104
+ | `CONFIG_OUT` | `./config_task_{N}.yaml` | Output path for generated config |
105
+ | `DATA_ROOT` | `./data` | Path to ARC data directory |
106
+ | `MAX_ITERATIONS` | (from config) | Override `max_iterations` at runtime |
107
+ | `ARC_EVAL_INCLUDE_TEST` | `0` | Set to `1` to also run the held-out test inputs during evolution |
108
+ | `ARC_EVAL_USE_TEST_FOR_SCORE` | `0` | Set to `1` to average train and test scores into `combined_score` (only used when `ARC_EVAL_INCLUDE_TEST=1`) |
configs/README.md ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ⚙️ Configuration
2
+
3
+ All settings are YAML. Environment variables can be referenced with `${VAR}` syntax in any string value.
4
+
5
+ ---
6
+
7
+ ## 📁 Available Config Files
8
+
9
+ | File | Search | Description |
10
+ |------|--------|-------------|
11
+ | **default.yaml** | Top-K | Minimal starting template — good for first experiments |
12
+ | **adaevolve.yaml** | AdaEvolve | Full multi-island config with adaptive intensity, migration, paradigm breakthroughs, and ablation flags |
13
+ | **evox.yaml** | EvoX | Co-evolving solution generation and search strategies |
14
+ | **openevolve_native.yaml** | OpenEvolve Native | Native port of OpenEvolve's island-based MAP-Elites search with ring migration |
15
+ | **llm_judge.yaml** | - | Demonstrates LLM-as-a-judge evaluation (uses gpt-4o-mini for both generation and judging) |
16
+ | **human_in_the_loop.yaml** | Top-K | Enables the live monitor dashboard and human-in-the-loop feedback |
17
+
18
+ Each file is a ready-to-copy template. Fill in the **system_message** with your problem description and you're good to go.
19
+
20
+ ---
21
+
22
+ ## 🔧 Parameter Reference
23
+
24
+ ### Top-level
25
+
26
+ ```yaml
27
+ max_iterations: 100 # total evolution iterations
28
+ checkpoint_interval: 10 # save checkpoint every N iterations
29
+ log_level: "INFO" # DEBUG / INFO / WARNING
30
+ log_dir: null # directory for logs (default: outputs/)
31
+ random_seed: 42
32
+ language: null # auto-detected from initial program ("python", "cpp", "java", etc.)
33
+ # set to "image" for image-generation mode
34
+ file_suffix: ".py" # output file extension, auto-set from initial program at runtime
35
+ diff_based_generation: true # LLM receives diffs instead of full programs
36
+ max_solution_length: 60000 # max characters in a program; longer programs are trimmed
37
+ ```
38
+
39
+ ### llm
40
+
41
+ ```yaml
42
+ llm:
43
+ temperature: 0.7
44
+ top_p: 0.95
45
+ max_tokens: 32000
46
+ timeout: 600 # seconds per LLM request
47
+ retries: 3
48
+ retry_delay: 5 # seconds between retries
49
+ random_seed: null
50
+ reasoning_effort: null # "low" / "medium" / "high" for o-series models
51
+ ```
52
+
53
+ **Model specification** — use `provider/model` or a bare name (auto-detected for known prefixes):
54
+
55
+ | Provider | Format | API key env var |
56
+ |----------|--------|-----------------|
57
+ | OpenAI | `gpt-5`, `o3-mini` | OPENAI_API_KEY |
58
+ | Gemini | `gemini/gemini-2.0-flash` | GEMINI_API_KEY or GOOGLE_API_KEY |
59
+ | Anthropic | `claude-sonnet-4-6` or `anthropic/claude-sonnet-4-6` | ANTHROPIC_API_KEY |
60
+ | DeepSeek | `deepseek-chat` or `deepseek/deepseek-chat` | DEEPSEEK_API_KEY |
61
+ | Mistral | `mistral-large` or `mistral/mistral-large` | MISTRAL_API_KEY |
62
+ | Ollama / vLLM | `ollama/llama3`, `vllm/my-model` | — |
63
+
64
+ <details>
65
+ <summary><b>Single model, multi-model pool, separate pools, and API override examples</b></summary>
66
+
67
+ **Single model (shorthand):**
68
+ ```yaml
69
+ llm:
70
+ primary_model: "gpt-5"
71
+ primary_model_weight: 1.0
72
+ ```
73
+
74
+ **Multi-model pool (weighted sampling):**
75
+ ```yaml
76
+ llm:
77
+ models:
78
+ - name: "gpt-5"
79
+ weight: 0.8
80
+ - name: "anthropic/claude-opus-4-6"
81
+ weight: 0.2
82
+ ```
83
+
84
+ **Separate model pools** — by default all pools share `models`; override individually:
85
+ ```yaml
86
+ llm:
87
+ models:
88
+ - name: "gpt-5"
89
+ evaluator_models: # used by LLM-as-a-judge (evaluator.use_llm_feedback)
90
+ - name: "gpt-4o-mini"
91
+ guide_models: # used for paradigm breakthroughs and variation labels
92
+ - name: "gpt-4o-mini"
93
+ ```
94
+
95
+ **Override API base:**
96
+ ```yaml
97
+ llm:
98
+ api_base: "https://my-proxy.example.com/v1"
99
+ api_key: "${MY_API_KEY}"
100
+ ```
101
+
102
+ You can also set OPENAI_API_BASE or OPENAI_BASE_URL env vars to override the config globally.
103
+
104
+ </details>
105
+
106
+ ### search
107
+
108
+ ```yaml
109
+ search:
110
+ type: "adaevolve" # evox | openevolve_native | beam_search | best_of_n | topk
111
+ num_context_programs: 4 # context programs shown to LLMs as examples
112
+ ```
113
+
114
+ <details>
115
+ <summary><b>topk</b> — no extra settings</summary>
116
+
117
+ Always picks the single best program as parent and the next K as additional context programs.
118
+
119
+ </details>
120
+
121
+ <details>
122
+ <summary><b>adaevolve</b> — full settings</summary>
123
+
124
+ ```yaml
125
+ search:
126
+ type: "adaevolve"
127
+ database:
128
+ population_size: 20
129
+ num_islands: 2
130
+
131
+ # Adaptive intensity
132
+ decay: 0.9 # EMA weight for accumulated signal G
133
+ intensity_min: 0.15 # min intensity (exploitation)
134
+ intensity_max: 0.5 # max intensity (exploration)
135
+
136
+ # Migration
137
+ migration_interval: 15 # migrate every N iterations
138
+ migration_count: 5 # top programs to copy between islands
139
+
140
+ # Archive diversity
141
+ fitness_weight: 1.0 # fitness contribution to elite score
142
+ novelty_weight: 0.0 # novelty contribution to elite score
143
+ diversity_strategy: "code" # "code" / "metric" / "hybrid"
144
+
145
+ # Dynamic island spawning
146
+ use_dynamic_islands: true
147
+ max_islands: 5
148
+ spawn_productivity_threshold: 0.015
149
+ spawn_cooldown_iterations: 30
150
+
151
+ # Paradigm breakthrough
152
+ use_paradigm_breakthrough: true
153
+ paradigm_window_size: 10
154
+ paradigm_improvement_threshold: 0.12
155
+ paradigm_num_to_generate: 3
156
+ paradigm_max_uses: 2
157
+ paradigm_max_tried: 10
158
+
159
+ # Error retry
160
+ enable_error_retry: true
161
+ max_error_retries: 2
162
+
163
+ # Ablation flags (set false to disable)
164
+ use_adaptive_search: true # G-based intensity; false → use fixed_intensity
165
+ use_ucb_selection: true # UCB island selection; false → round-robin
166
+ use_migration: true
167
+ use_unified_archive: true # quality-diversity archive; false → simple list
168
+ ```
169
+
170
+ </details>
171
+
172
+ <details>
173
+ <summary><b>evox</b></summary>
174
+
175
+ ```yaml
176
+ search:
177
+ type: "evox"
178
+ database:
179
+ auto_generate_variation_operators: true # by default generate variation operator once
180
+ ```
181
+
182
+ </details>
183
+
184
+ <details>
185
+ <summary><b>beam_search</b></summary>
186
+
187
+ ```yaml
188
+ search:
189
+ type: "beam_search"
190
+ database:
191
+ beam_width: 5
192
+ beam_selection_strategy: "diversity_weighted" # diversity_weighted / stochastic / round_robin / best
193
+ beam_diversity_weight: 0.3
194
+ beam_temperature: 1.0
195
+ beam_depth_penalty: 0.0
196
+ ```
197
+
198
+ </details>
199
+
200
+ <details>
201
+ <summary><b>best_of_n</b></summary>
202
+
203
+ ```yaml
204
+ search:
205
+ type: "best_of_n"
206
+ database:
207
+ best_of_n: 5 # reuse the same parent for N iterations, then switch to current best
208
+ ```
209
+
210
+ </details>
211
+
212
+ <details>
213
+ <summary><b>openevolve_native</b> — MAP-Elites + island-based evolutionary search</summary>
214
+
215
+ Native port of [OpenEvolve](https://github.com/codelion/openevolve)'s search algorithm.
216
+ Uses MAP-Elites quality-diversity grid per island with ring-topology migration.
217
+
218
+ ```yaml
219
+ search:
220
+ type: "openevolve_native"
221
+ num_context_programs: 5
222
+ database:
223
+ num_islands: 5
224
+ population_size: 40
225
+ archive_size: 100
226
+ exploration_ratio: 0.2 # P(explore) — random from current island
227
+ exploitation_ratio: 0.7 # P(exploit) — archive elite, prefer current island
228
+ # remaining 0.1 = P(random) — any program in population
229
+ elite_selection_ratio: 0.1 # fraction of additional context programs from top elites
230
+ feature_dimensions: ["complexity", "diversity"]
231
+ feature_bins: 10
232
+ diversity_reference_size: 20
233
+ migration_interval: 10 # migrate every N island-generations
234
+ migration_rate: 0.1 # fraction of island to migrate
235
+ random_seed: 42
236
+ ```
237
+
238
+ See [`skydiscover/search/openevolve_native/README.md`](../skydiscover/search/openevolve_native/README.md) for architecture details.
239
+
240
+ </details>
241
+
242
+ ### prompt
243
+
244
+ ```yaml
245
+ prompt:
246
+ system_message: |
247
+ You are an expert coder helping to improve programs through evolution.
248
+
249
+ # system_message can also be a path to a .txt file (relative to the config):
250
+ # system_message: "system_prompt.txt"
251
+
252
+ evaluator_system_message: | # system message for the LLM judge
253
+ You are a strict code quality judge. ... # only used when evaluator.use_llm_feedback: true
254
+
255
+ suggest_simplification_after_chars: 500 # threshold for program labeling in prompts
256
+ ```
257
+
258
+ ### evaluator
259
+
260
+ ```yaml
261
+ evaluator:
262
+ timeout: 360 # seconds before killing evaluate() subprocess
263
+ max_retries: 3
264
+
265
+ # Cascade evaluation: skip expensive full eval on low-scoring programs
266
+ cascade_evaluation: true
267
+ cascade_thresholds: [0.3, 0.6]
268
+
269
+ # Prepend evaluator source code (or instruction.md for Harbor tasks)
270
+ # to the LLM system message so the model can see how solutions are scored.
271
+ inject_evaluator_context: false # default false
272
+
273
+ # LLM-as-a-judge
274
+ use_llm_feedback: false
275
+ llm_feedback_weight: 1.0 # relative weight of LLM score in combined_score
276
+ ```
277
+
278
+ ### agentic
279
+
280
+ Multi-turn agent that can read files and search the codebase before generating solutions.
281
+ Enable via `--agentic` on the CLI or `agentic=True` in `run_discovery()`. The codebase root
282
+ defaults to the initial program's directory; override it here if needed.
283
+
284
+ ```yaml
285
+ agentic:
286
+ enabled: false
287
+ codebase_root: null # defaults to initial program's directory when omitted
288
+ max_steps: 5 # max tool-call turns per iteration
289
+ per_step_timeout: 60.0 # seconds per tool call
290
+ overall_timeout: 300.0 # total seconds for one agentic generation
291
+ max_context_chars: 400000
292
+ max_file_chars: 50000
293
+ max_files_read: 20
294
+ max_search_results: 50
295
+ ```
296
+
297
+ ### monitor
298
+
299
+ Live dashboard served over WebSocket.
300
+
301
+ ```yaml
302
+ monitor:
303
+ enabled: false
304
+ port: 8765
305
+ host: "0.0.0.0"
306
+ max_solution_length: 10000
307
+
308
+ # AI-generated run summaries
309
+ summary_model: "gpt-5-mini"
310
+ summary_api_key: null # falls back to OPENAI_API_KEY
311
+ summary_top_k: 3
312
+ summary_interval: 0 # auto-generate every N programs (0 = manual only)
313
+ ```
314
+
315
+ ### human_feedback
316
+
317
+ ```yaml
318
+ human_feedback_enabled: false
319
+ human_feedback_file: null # path to a file containing feedback text
320
+ human_feedback_mode: "append" # "append" or "replace"
321
+ ```
322
+
323
+ ---
324
+
325
+ ## 🚀 Getting Started
326
+
327
+ **1. Pick a template** — copy one of the config files above into your project directory:
328
+
329
+ ```bash
330
+ cp configs/evox.yaml my_config.yaml
331
+ ```
332
+
333
+ **2. Fill in the system message** — this is the most important field. Tell the LLM what problem it's solving:
334
+
335
+ ```yaml
336
+ prompt:
337
+ system_message: |
338
+ You are an expert at optimizing circle packing algorithms.
339
+ Maximize the number of non-overlapping circles in a unit square.
340
+ ```
341
+
342
+ **3. Run with your config:**
343
+
344
+ ```bash
345
+ uv run skydiscover-run initial_program.py evaluator.py -c my_config.yaml -i 100
346
+ ```
347
+
348
+ You can override any config value from the CLI — for example, switch the search algorithm or model without editing the YAML:
349
+
350
+ ```bash
351
+ uv run skydiscover-run initial_program.py evaluator.py \
352
+ -c my_config.yaml \
353
+ --model gemini/gemini-3-pro-preview \
354
+ -i 50
355
+ ```
configs/adaevolve.yaml ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AdaEvolve - Adaptive Evolutionary Search Configuration
2
+ #
3
+ # AdaEvolve is an adaptive multi-island evolutionary algorithm that adjusts
4
+ # search intensity per island based on accumulated improvement history.
5
+ #
6
+ # Key features:
7
+ # - Per-island adaptive exploration/exploitation via accumulated signal (G)
8
+ # - UCB with decayed magnitude rewards for island selection
9
+ # - UnifiedArchive per island for quality-diversity balance
10
+ # - Ring migration for cross-pollination between islands
11
+ # - Dynamic island spawning when global productivity drops
12
+ # - Paradigm breakthrough for high-level strategy shifts during stagnation
13
+ #
14
+ # Usage:
15
+ # Copy this file to your example directory as config_adaevolve.yaml
16
+ # and fill in the system_message with your problem description.
17
+
18
+ # General settings
19
+ max_iterations: 100
20
+ checkpoint_interval: 10
21
+ log_level: "INFO"
22
+ random_seed: 42
23
+
24
+ # LLM configuration
25
+ llm:
26
+ models:
27
+ - name: "gpt-5"
28
+ weight: 1.0
29
+ api_base: "https://api.openai.com/v1"
30
+ temperature: 0.7
31
+ # top_p: 0.95 # omitted by default; some providers (e.g. Anthropic) reject both temperature and top_p
32
+ max_tokens: 32000
33
+ timeout: 600
34
+ # To use Gemini: comment out models + api_base above, uncomment below
35
+ # models:
36
+ # - name: "gemini-3-pro-preview"
37
+ # api_base: "https://generativelanguage.googleapis.com/v1beta/openai/"
38
+ # api_key: ${GEMINI_API_KEY}
39
+
40
+ # Search configuration
41
+ search:
42
+ type: "adaevolve"
43
+ num_context_programs: 4
44
+ database:
45
+ # Population settings
46
+ population_size: 20
47
+ num_islands: 2
48
+
49
+ # --- Adaptive search intensity ---
50
+ # Controls how search intensity adapts based on improvement signal (G)
51
+ # High G (productive island) -> low intensity (exploit)
52
+ # Low G (stagnating island) -> high intensity (explore)
53
+ decay: 0.9 # Exponential moving average weight (rho)
54
+ intensity_min: 0.15 # Minimum search intensity (exploitation)
55
+ intensity_max: 0.5 # Maximum search intensity (exploration)
56
+
57
+ # --- Ablation flags ---
58
+ # Set to false to disable specific adaptive mechanisms for ablation studies
59
+ use_adaptive_search: true # G-based intensity vs fixed_intensity
60
+ use_ucb_selection: true # UCB island selection vs round-robin
61
+ use_migration: true # Inter-island migration
62
+ use_unified_archive: true # Quality-diversity archive vs simple list
63
+ # fixed_intensity: 0.4 # Used when use_adaptive_search=false
64
+
65
+ # --- Migration ---
66
+ migration_interval: 15 # Migrate every N iterations
67
+ migration_count: 5 # Number of top programs to migrate
68
+
69
+ # --- Archive settings (when use_unified_archive=true) ---
70
+ fitness_weight: 1.0 # Weight for fitness rank in elite score
71
+ novelty_weight: 0.0 # Weight for novelty rank in elite score
72
+ diversity_strategy: "code" # Diversity metric: "code", "metric", or "hybrid"
73
+
74
+ # --- Optional Pareto multiobjective mode ---
75
+ # Leave pareto_objectives empty to keep the current scalar combined_score behavior.
76
+ # When enabled, return the raw objective metrics from the evaluator and set:
77
+ # pareto_objectives:
78
+ # - accuracy
79
+ # - latency
80
+ # higher_is_better:
81
+ # accuracy: true
82
+ # latency: false
83
+ # fitness_key: accuracy # Optional scalar proxy for adaptive state / tie-breaking
84
+ # pareto_objectives_weight: 0.4 # Weight of Pareto percentile in archive elite score
85
+
86
+ # --- Dynamic island spawning ---
87
+ use_dynamic_islands: true
88
+ max_islands: 5 # Maximum number of islands
89
+ spawn_productivity_threshold: 0.015 # Spawn if global productivity below this
90
+ spawn_cooldown_iterations: 30 # Wait N iterations between spawns
91
+
92
+ # --- Paradigm breakthrough ---
93
+ use_paradigm_breakthrough: true
94
+ paradigm_window_size: 10 # Window for improvement rate calculation
95
+ paradigm_improvement_threshold: 0.12 # Stagnation threshold
96
+ paradigm_max_uses: 2 # Max uses per paradigm
97
+ paradigm_max_tried: 10 # Max tried paradigms to track
98
+ paradigm_num_to_generate: 3 # Number of paradigms to generate per trigger
99
+
100
+ # --- Error retry ---
101
+ enable_error_retry: true
102
+ max_error_retries: 2
103
+
104
+ # Prompt configuration
105
+ prompt:
106
+ system_message: |
107
+ <REPLACE WITH YOUR PROBLEM-SPECIFIC SYSTEM MESSAGE>
108
+
109
+ Describe:
110
+ - What the algorithm/function should do
111
+ - Input/output format
112
+ - Optimization objective and metrics
113
+ - Constraints
114
+ - Techniques to explore
115
+
116
+ # Evaluator configuration
117
+ evaluator:
118
+ timeout: 360
119
+ max_retries: 3
120
+ cascade_evaluation: true
121
+ cascade_thresholds: [0.3, 0.6]
122
+
123
+ # Generation settings
124
+ diff_based_generation: true
125
+ max_solution_length: 60000
configs/default.yaml ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # General settings
2
+ max_iterations: 100
3
+ checkpoint_interval: 10
4
+ log_level: "INFO"
5
+ random_seed: 42
6
+
7
+ # LLM configuration
8
+ llm:
9
+ models:
10
+ - name: "gpt-5"
11
+ weight: 1.0
12
+ temperature: 0.7
13
+ # top_p: 0.95 # omitted by default; some providers (e.g. Anthropic) reject both temperature and top_p
14
+ max_tokens: 32000
15
+ timeout: 600
16
+
17
+ # Database configuration
18
+ search:
19
+ # Read from the evolved file and then use the evolve algorithm to evolve the program
20
+ type: "topk"
21
+ database:
22
+ random_seed: 42
23
+ num_context_programs: 4
24
+
25
+ # Prompt configuration
26
+ prompt:
27
+ system_message: "You are an expert to help find the best solution to the problem."
28
+
29
+
30
+ # Evaluator configuration
31
+ evaluator:
32
+ timeout: 10000
33
+ max_retries: 3
34
+ cascade_evaluation: false
35
+
36
+ # Generation settings
37
+ diff_based_generation: true
38
+ max_solution_length: 60000
configs/evox.yaml ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EvoX - Label-guided Evolutionary Search Configuration
2
+ #
3
+ # EvoX is a co-evolutionary search strategy that uses label-guided
4
+ # exploration and exploitation to evolve programs.
5
+ #
6
+ # Usage:
7
+ # Copy this file to your example directory as config_evox.yaml
8
+ # and fill in the system_message with your problem description.
9
+
10
+ # General settings
11
+ max_iterations: 100
12
+ checkpoint_interval: 10
13
+ log_level: "INFO"
14
+ random_seed: 42
15
+
16
+ # LLM configuration
17
+ llm:
18
+ models:
19
+ - name: "gpt-5"
20
+ weight: 1.0
21
+ api_base: "https://api.openai.com/v1"
22
+ temperature: 0.7
23
+ # top_p: 0.95 # omitted by default; some providers (e.g. Anthropic) reject both temperature and top_p
24
+ max_tokens: 32000
25
+ timeout: 600
26
+ # To use Gemini: comment out models + api_base above, uncomment below
27
+ # models:
28
+ # - name: "gemini-3-pro-preview"
29
+ # api_base: "https://generativelanguage.googleapis.com/v1beta/openai/"
30
+ # api_key: ${GEMINI_API_KEY}
31
+
32
+ # Search configuration
33
+ search:
34
+ type: "evox"
35
+ database:
36
+ auto_generate_variation_operators: true
37
+
38
+ # Prompt configuration
39
+ prompt:
40
+ system_message: |
41
+ <REPLACE WITH YOUR PROBLEM-SPECIFIC SYSTEM MESSAGE>
42
+
43
+ Describe:
44
+ - What the algorithm/function should do
45
+ - Input/output format
46
+ - Optimization objective and metrics
47
+ - Constraints
48
+ - Techniques to explore
49
+
50
+ # Evaluator configuration
51
+ evaluator:
52
+ timeout: 360
53
+ max_retries: 3
54
+ cascade_evaluation: true
55
+ cascade_thresholds: [0.3, 0.6]
56
+
57
+ # Generation settings
58
+ diff_based_generation: true
59
+ max_solution_length: 60000
configs/human_in_the_loop.yaml ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Human-in-the-loop configuration
2
+ # Enables the live monitor dashboard and human feedback.
3
+ # Usage: skydiscover-run program.py evaluator.py -c configs/human_in_the_loop.yaml
4
+
5
+ # General settings
6
+ max_iterations: 100
7
+ checkpoint_interval: 10
8
+ log_level: "INFO"
9
+ random_seed: 42
10
+
11
+ # LLM configuration
12
+ llm:
13
+ models:
14
+ - name: "gpt-5"
15
+ weight: 1.0
16
+ temperature: 0.7
17
+ # top_p: 0.95 # omitted by default; some providers (e.g. Anthropic) reject both temperature and top_p
18
+ max_tokens: 32000
19
+ timeout: 600
20
+
21
+ # Search configuration
22
+ search:
23
+ type: "topk"
24
+ database:
25
+ random_seed: 42
26
+ num_context_programs: 4
27
+
28
+ # Prompt configuration
29
+ prompt:
30
+ system_message: "You are an expert to help find the best solution to the problem."
31
+
32
+ # Evaluator configuration
33
+ evaluator:
34
+ timeout: 10000
35
+ max_retries: 3
36
+ cascade_evaluation: false
37
+
38
+ # Generation settings
39
+ diff_based_generation: true
40
+ max_solution_length: 60000
41
+
42
+ # Live monitor dashboard
43
+ monitor:
44
+ enabled: true
45
+ port: 8765
46
+ host: "127.0.0.1"
47
+
48
+ # Human feedback
49
+ human_feedback_enabled: true
configs/llm_judge.yaml ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Minimal config to test LLM-as-a-judge evaluation feedback
2
+ # Usage: skydiscover-run initial_program.py evaluator.py -c configs/llm_judge.yaml
3
+
4
+ max_iterations: 10
5
+ checkpoint_interval: 5
6
+ log_level: "DEBUG"
7
+
8
+ llm:
9
+ models:
10
+ - name: "gpt-4o-mini"
11
+ weight: 1.0
12
+ temperature: 0.7
13
+ max_tokens: 16000
14
+ timeout: 120
15
+
16
+ # Model used by the evaluator's LLM judge
17
+ evaluator_models:
18
+ - name: "gpt-4o-mini"
19
+ weight: 1.0
20
+
21
+ search:
22
+ type: "topk"
23
+ num_context_programs: 2
24
+
25
+ prompt:
26
+ system_message: "You are an expert programmer. Improve the given program."
27
+ evaluator_system_message: >
28
+ You are a strict code quality judge. Evaluate the given code and return a JSON object
29
+ with scores between 0.0 and 1.0 for each metric. Be critical — only exceptional code
30
+ should score above 0.8. Consider algorithmic correctness, edge case handling, and
31
+ real-world production readiness in your assessment.
32
+
33
+ evaluator:
34
+ timeout: 60
35
+ max_retries: 1
36
+ cascade_evaluation: false
37
+ llm_as_judge: true
38
+
39
+ diff_based_generation: true
40
+ max_solution_length: 10000
configs/openevolve_native.yaml ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OpenEvolve Native — MAP-Elites + Island-based Evolutionary Search
2
+ #
3
+ # A faithful port of the OpenEvolve search algorithm:
4
+ # - MAP-Elites grid per island for quality-diversity
5
+ # - Island-based population with round-robin rotation
6
+ # - Ring-topology migration between islands
7
+ # - Exploration / exploitation / random parent sampling
8
+ # - Greedy-diverse context program sampling from nearby MAP-Elites cells
9
+ #
10
+ # Usage:
11
+ # skydiscover-run initial_program.py evaluator.py \
12
+ # -s openevolve_native -m gpt-5-mini -i 100
13
+
14
+ # General settings
15
+ max_iterations: 100
16
+ checkpoint_interval: 10
17
+ log_level: "INFO"
18
+
19
+ # LLM configuration
20
+ llm:
21
+ models:
22
+ - name: "gpt-5"
23
+ weight: 1.0
24
+ api_base: "https://api.openai.com/v1"
25
+ temperature: 0.7
26
+ # top_p: 0.95 # omitted by default; some providers (e.g. Anthropic) reject both temperature and top_p
27
+ max_tokens: 32000
28
+ timeout: 600
29
+
30
+ # Search configuration
31
+ search:
32
+ type: "openevolve_native"
33
+ num_context_programs: 5
34
+ database:
35
+ num_islands: 5
36
+ population_size: 40
37
+ archive_size: 100
38
+ exploration_ratio: 0.2 # P(explore) — random from current island
39
+ exploitation_ratio: 0.7 # P(exploit) — archive elite, prefer current island
40
+ # remaining 0.1 = P(random) — any program in population
41
+ elite_selection_ratio: 0.1 # fraction of context programs from top elites
42
+ feature_dimensions: ["complexity", "diversity"]
43
+ feature_bins: 10
44
+ diversity_reference_size: 20
45
+ migration_interval: 10 # migrate every N island-generations
46
+ migration_rate: 0.1 # fraction of island to migrate
47
+ random_seed: 42
48
+
49
+ # Prompt configuration
50
+ prompt:
51
+ system_message: |
52
+ <REPLACE WITH YOUR PROBLEM-SPECIFIC SYSTEM MESSAGE>
53
+
54
+ Describe:
55
+ - What the algorithm/function should do
56
+ - Input/output format
57
+ - Optimization objective and metrics
58
+ - Constraints
59
+ - Techniques to explore
60
+
61
+ # Evaluator configuration
62
+ evaluator:
63
+ timeout: 360
64
+ max_retries: 3
65
+ cascade_evaluation: true
66
+ cascade_thresholds: [0.3, 0.6]
67
+
68
+ # Generation settings
69
+ diff_based_generation: true
70
+ max_solution_length: 60000
pyproject.toml ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "skydiscover"
7
+ dynamic = ["version"]
8
+ description = "A Flexible Framework for AI-Driven Scientific and Algorithmic Discovery"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10, <3.14"
11
+ license = {text = "Apache-2.0"}
12
+ dependencies = [
13
+ "openai>=1.0.0",
14
+ "pyyaml>=6.0",
15
+ "tqdm>=4.64.0",
16
+ "numpy>=1.22.0",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ math = [
21
+ "scipy>=1.11.0",
22
+ "sympy>=1.14.0",
23
+ "jax>=0.6.2",
24
+ "optax>=0.2.6",
25
+ "torch",
26
+ "scikit-learn>=1.0.0",
27
+ "numba",
28
+ "pandas",
29
+ "matplotlib",
30
+ "plotly",
31
+ "networkx",
32
+ "cvxpy",
33
+ "autograd",
34
+ "pymoo",
35
+ "PyWavelets",
36
+ ]
37
+ adrs = [
38
+ "numpy>=1.22.0",
39
+ "pandas",
40
+ "networkx>=3.2,<3.4",
41
+ "torch",
42
+ ]
43
+ external = [
44
+ "openevolve",
45
+ "gepa[full]",
46
+ "litellm>=1.81", # gepa[full] needs litellm, but uv override-dependencies strips the [full] extra
47
+ ]
48
+ dev = [
49
+ "pytest>=7.0.0",
50
+ "pytest-asyncio>=0.21.0",
51
+ "black>=22.0.0",
52
+ "isort>=5.10.0",
53
+ "mypy>=0.950",
54
+ "requests>=2.28.0",
55
+ ]
56
+ frontier-cs = [
57
+ "anthropic>=0.74.0",
58
+ "colorlog>=6.10.1",
59
+ "datasets>=4.4.1",
60
+ "google-genai>=1.55.0",
61
+ "google-generativeai>=0.8.5",
62
+ "numpy>=2.0.0", # Frontier-CS requires numpy 2.x
63
+ "python-dotenv>=1.2.1",
64
+ "skypilot>=0.10.5",
65
+ ]
66
+ prompt-optimization = [
67
+ "dspy>=3.1.3",
68
+ "litellm>=1.81",
69
+ "bm25s>=0.3.0",
70
+ "pystemmer>=2.2.0.3",
71
+ "datasets>=4.5.0",
72
+ "diskcache>=5.6.3",
73
+ "ujson>=5.11.0",
74
+ ]
75
+
76
+ [tool.uv]
77
+ override-dependencies = ["httpx>=0.28.1", "gepa>=0.0.26"]
78
+
79
+ [tool.uv.sources]
80
+ openevolve = { git = "https://github.com/algorithmicsuperintelligence/openevolve.git", branch = "main" }
81
+ gepa = { git = "https://github.com/gepa-ai/gepa.git", branch = "main" }
82
+
83
+ [tool.black]
84
+ line-length = 100
85
+ target-version = ['py310']
86
+ include = '\.pyi?$'
87
+
88
+ [tool.isort]
89
+ profile = "black"
90
+ line_length = 100
91
+
92
+ [tool.mypy]
93
+ python_version = "3.10"
94
+ warn_return_any = true
95
+ warn_unused_configs = true
96
+ disallow_untyped_defs = true
97
+ disallow_incomplete_defs = true
98
+
99
+ [project.scripts]
100
+ skydiscover-run = "skydiscover.cli:main"
101
+ skydiscover-viewer = "skydiscover.extras.monitor.viewer:main"
102
+
103
+ [tool.pytest.ini_options]
104
+ testpaths = ["tests"]
105
+ markers = [
106
+ "slow: marks tests as slow (deselect with '-m \"not slow\"')",
107
+ "integration: marks tests as integration tests requiring external services"
108
+ ]
109
+ addopts = "--strict-markers"
110
+
111
+ [tool.setuptools.packages.find]
112
+ include = ["skydiscover*"]
113
+
114
+ [tool.setuptools.package-data]
115
+ skydiscover = [
116
+ "context_builder/*/templates/*.txt",
117
+ "search/evox/config/*.yaml",
118
+ "search/evox/config/*.txt",
119
+ "extras/external/defaults/*.yaml",
120
+ "extras/monitor/dashboard.html",
121
+ ]
122
+
123
+ [tool.setuptools.dynamic]
124
+ version = {attr = "skydiscover._version.__version__"}