Interactive Mind Games
The results of all rounds of games for this problem are available.
The Logical Acumen company wants to build AGI (who doesn't?). They decided that the large advantage of AGI should be the ability to win games with zero knowledge. For example, they want AGI to win in a multiplayer game where each player gives a number and the smallest unique value wins.
However, there is no training data yet. To solve this issue they prepared Interactive Mind Games — a collection of three independent games played against other teams
throughout the contest. Each game has its own rules, API, raw score, and scoreboard.
Unfortunately, before participating in Midnight Code Cup you signed Terms and Conditions without reading and, thus, have to play these games!
Three games are described one after another below:
Common Rules
Authentication
Requests that act on behalf of your team must include your API token:
Authorization: Bearer <your-api-token>
API
All games are accessed through a single API root: https://mind-games.midnightcodecup.org/
Each game's endpoints live under /api/v1/games/<game-slug>/, where <game-slug> is the slug of
the game (for example, blotto). The paths shown in each game section below are relative to this
API root.
A browser interface for playing each game is also available at /games/<game-slug>/.
Rate Limits
Each game limits authenticated requests to 30 requests per 60 seconds per team API token. Exceeding the limit returns 429 Too Many Requests. Limits
are tracked independently for each game.
Rounds and Round Weight
The contest interval is divided into rounds of one minute. A configuration endpoint returns the expected number of rounds during the contest in total_rounds.
Later rounds contribute higher scores than earlier ones. Let be the total number of rounds and let be the current round number, indexed from . The weight of the round is:
The round is considered complete and contributes to the score if the server is alive at the end of the minute.
Scoreboard Scoring
The maximum score per game is 400 points. You receive a raw score for each round that is accumulated to your total raw score for the game. Your score for the game is calculated as if is nonzero, and it is zero otherwise.
Game 1: Unique Smallest
Unique Smallest is played in synchronized rounds. In every round, each team may submit one integer. Numbers that were chosen by fewer teams rank ahead of more popular numbers; among equally popular numbers, the smaller number ranks ahead.
Rounds
The contest interval is divided into rounds of one minute. Use the configuration and status endpoints described below to obtain the authoritative contest times, the round duration, the current round, and the server time.
Your submission for a round must be an integer from to , inclusive. You may replace it while the round is active and only the last number received by the server before the round ends is used. If you do not submit a valid number during a round, you receive no raw score for that round.
Round boundaries are determined by the game server’s clock. Please be aware of network latency when submitting close to the end of a round.
Ranking
At the end of a round, submitted numbers are grouped by value. The groups are ordered by:
- frequency in ascending order;
- submitted number in ascending order.
All teams that submitted the same number occupy consecutive positions and receive their average rank.
For example, suppose the submitted numbers are:
1, 2, 2, 5, 5, 9
The groups are ordered as 1, 9, 2, 5. Their frequencies and ranks are:
| Number | Frequency | Rank |
|---|---|---|
| 1 | 1 | 1 |
| 9 | 1 | 2 |
| 2 | 2 | 3.5 |
| 5 | 2 | 5.5 |
Thus, being unique is more important than choosing a small number. The number itself breaks ties only between groups with the same frequency.
Raw Score
This game uses the common round weight (see Rounds and Round Weight). Let be your rank in a round. Your raw score for that round is:
Your total raw score for Unique Smallest is the sum of your raw scores over all rounds.
API
This game's endpoints live under /api/v1/games/unique-smallest/ on the shared API root described
in the Common Rules. The paths below are relative to that root.
Get Game Configuration
GET /api/v1/games/unique-smallest/config
This endpoint returns the fixed problem interval and round configuration:
{
"game_slug": "unique-smallest",
"problem_start_time": "2026-07-04T15:00:00+00:00",
"problem_end_time": "2026-07-05T10:00:00+00:00",
"round_duration_seconds": 60,
"total_rounds": 1140
}
Get Current Status
GET /api/v1/games/unique-smallest/status
The response contains the server time, current round, its start time, and the time remaining until the next round boundary:
{
"game_slug": "unique-smallest",
"server_time": "2026-07-04T15:41:45+00:00",
"current_round": 42,
"current_round_started_at": "2026-07-04T15:41:00+00:00",
"next_round_at": "2026-07-04T15:42:00+00:00",
"seconds_to_next_round": 15
}
Submit or Replace a Number
POST /api/v1/games/unique-smallest/move
Authorization: Bearer <your-api-token>
Content-Type: application/json
{
"number": 42
}
A successful response confirms the accepted number and round:
{
"status": "accepted",
"game_slug": "unique-smallest",
"round_number": 42,
"accepted_number": 42,
"next_round_at": "2026-07-04T15:42:00+00:00"
}
Inspect the Latest Completed Round
GET /api/v1/games/unique-smallest/last-round
Authorization: Bearer <your-api-token>
The endpoint returns the latest completed round that had at least one submission. numbers
contains all submitted numbers in sorted order, including duplicates. Team identities are not
revealed. your_move is null without a valid ranked submission. Otherwise, it contains your
number and your rank, rank_score before round weighting, and the weighted raw_score added to your
raw total.
{
"round": 41,
"numbers": [1, 2, 2, 5, 5, 42],
"your_move": {
"number": 42,
"rank": 6.0,
"rank_score": 0.32768,
"raw_score": 0.35255
}
}
Game 2: Blotto
Blotto is played in synchronized rounds. In every round, each team distributes exactly troops among numbered battlefields. After the round ends, every pair of participating teams fights a duel using their submitted allocations.
Rounds and Submissions
Your allocation must contain exactly five non-negative integers whose sum is exactly . Battlefields are compared by position, so the first number is assigned to battlefield , the second number to battlefield , and so on.
For example, below we provide a valid allocation:
42, 42, 8, 5, 3
You may change your allocation while the round is active. Only the last valid allocation received by the server before the round ends is used. A team that does not submit a valid allocation does not participate in that round and receives no raw score for it.
Duels and Ranking
Each participating team plays one duel against every other participating team. For each battlefield:
- the team that assigned more troops wins that battlefield;
- if both teams assigned the same number of troops, neither team wins that battlefield.
The team that wins more battlefields wins the duel and receives battle point. If both teams win the same number of battlefields, each receives battle points.
For example, consider these allocations:
Team A: 42, 40, 5, 3, 10
Team B: 1, 42, 5, 42, 10
Team A wins battlefield , Team B wins battlefields and , and battlefields and are tied. As a result, Team B wins more battlefields and gets battle point.
A team's battle score is the sum of its battle points over all duels in the round. Teams are ranked by battle score in descending order. Teams with equal battle scores occupy consecutive positions and receive the average rank of those positions.
Raw Score
Blotto uses the same rank-based scoring as Unique Smallest: the common round weight (see Rounds and Round Weight) and a rank decay of , where is your rank in the round.
Your raw score for Blotto is the sum of your raw scores over all rounds.
Round Feedback
After a round, the server returns aggregate feedback without revealing another team's allocation or identity.
teams_count is the number of teams that submitted valid allocations in that round.
your_move is null if you did not participate. Otherwise, it contains your allocation,
rank, score fields, and feedback.
If you participated, field_dominance contains one integer for each battlefield. For every
opponent, the value for a battlefield is increased by:
- if your allocation is greater than the opponent's allocation;
- if the allocations are equal;
- if your allocation is smaller.
Therefore, for a participating team, the maximum possible value for one battlefield is . A larger value means that your allocation on that battlefield was greater than more opponents' allocations.
Example feedback:
{
"round": 42,
"teams_count": 4,
"your_move": {
"allocation": [42, 42, 8, 5, 3],
"field_dominance": [5, 3, 3, 2, 1],
"rank": 2.0,
"rank_score": 0.8,
"raw_score": 0.86229
}
}
API
This game's endpoints live under /api/v1/games/blotto/ on the shared API root described in the
Common Rules. The paths below are relative to that root.
Get Game Configuration
GET /api/v1/games/blotto/config
The response contains the fixed problem interval, round duration, total number of rounds, number of battlefields, and troop budget:
{
"game_slug": "blotto",
"problem_start_time": "2026-07-04T15:00:00+00:00",
"problem_end_time": "2026-07-05T10:00:00+00:00",
"round_duration_seconds": 60,
"total_rounds": 1140,
"num_fields": 5,
"total_troops": 100
}
Get Current Status
GET /api/v1/games/blotto/status
The response contains the server time, current round, its start time, and the time remaining until
the next round boundary. Its JSON fields have the same names and meanings as the Unique Smallest
status response shown above, with game_slug set to blotto.
Submit or Replace an Allocation
POST /api/v1/games/blotto/move
Authorization: Bearer <your-api-token>
Content-Type: application/json
{
"allocation": [42, 42, 8, 5, 3]
}
A successful response confirms the allocation and round:
{
"status": "accepted",
"game_slug": "blotto",
"round_number": 42,
"accepted_allocation": [42, 42, 8, 5, 3],
"next_round_at": "2026-07-04T15:42:00+00:00"
}
Inspect the Latest Completed Round
GET /api/v1/games/blotto/last-round
Authorization: Bearer <your-api-token>
The endpoint returns the latest completed round that had at least one submission, together with
teams_count and your_move as described above. In your_move, rank_score is the value
before round weighting and raw_score is the weighted value added to your raw total.
Game 3: Door Rush
Door Rush is a two-phase crowd-prediction game played across six rooms arranged in a circle. Every room has a capacity and a reward. A room contributes a nonzero raw score only when the number of teams ending there does not exceed its capacity.
Round Setup and Phases
The round duration is returned by the configuration endpoint. Phase 1 occupies the first half of
each round and phase 2 occupies the second half. In phase 1, choose an entrance from 0 through
5 and a mode: rush or flex. Rush teams are locked into their entrance room. Flex teams may
use phase 2 to inspect anonymous entrance counts and choose a final room. A flex team that does
not submit a phase-2 room stays at its entrance.
At the start of a round, each room receives an integer capacity from 1 through 10. The total
capacity across all rooms is between 25 and 50. Rewards are based on capacity with small random
variation, so smaller rooms are generally more valuable and riskier. The public round state also
contains a rush bonus between 1.1 and 2.0.
Room parameters and rewards are generated using the following code:
ROOMS = 6
DECAY = 0.75
MIN_TOTAL_CAPACITY = 25
MAX_TOTAL_CAPACITY = 50
MIN_ROOM_CAPACITY = 1
MAX_ROOM_CAPACITY = 10
MIN_RUSH_BONUS = 1.1
MAX_RUSH_BONUS = 2.0
capacities = []
while not MIN_TOTAL_CAPACITY <= sum(capacities) <= MAX_TOTAL_CAPACITY:
capacities = [randint(MIN_ROOM_CAPACITY, MAX_ROOM_CAPACITY) for _ in range(ROOMS)]
rewards = []
for capacity in capacities:
base_reward = 50 + 25 * (MAX_ROOM_CAPACITY - capacity)
reward = base_reward + randint(-10, 10)
rewards.append(reward)
rush_bonus = round(uniform(MIN_RUSH_BONUS, MAX_RUSH_BONUS), 1)
After phase 1, flex teams may request entrance counts for each door. The counts include both rush and flex teams, but reveal no identities, modes, or final-room choices.
After that, each flex team can choose another door and send the final decision.
Each phase takes 30 seconds and is determined by the game server's clock.
Resolution and Raw Score
After phase 2, the server counts every team's final room. If a room is overcrowded, i.e., there are more teams that chose it than its capacity, every team in that room scores zero. Otherwise, its reward is:
For a flex team, circular distance is the shorter distance around the six-room circle between the room chosen during phase-1 and the room chosen during phase-2. Its reward is:
A team without a valid phase-1 submission receives zero. Unlike the rank-based games, Door Rush does not apply rank decay: the formula above produces the team's direct reward for the round. The common round weight (see Rounds and Round Weight) still applies:
API
This game's endpoints live under /api/v1/games/door-rush/ on the shared API root described in
the Common Rules. The paths below are relative to that root.
Get Game Configuration
GET /api/v1/games/door-rush/config
The response contains the round timing and every parameter used by the setup and scoring formulas:
{
"game_slug": "door-rush",
"problem_start_time": "2026-07-04T15:00:00+00:00",
"problem_end_time": "2026-07-05T10:00:00+00:00",
"round_duration_seconds": 60,
"total_rounds": 1140,
"rooms": 6,
"decay": 0.75,
"round_growth": 10.0,
"min_total_capacity": 25,
"max_total_capacity": 50,
"min_room_capacity": 1,
"max_room_capacity": 10,
"min_rush_bonus": 1.1,
"max_rush_bonus": 2.0
}
Get Current Status
GET /api/v1/games/door-rush/status
The public status endpoint has the same round timing fields as the other games. Door Rush-specific
data is returned in game_state. seconds_to_next_phase counts down to the middle of the round in
phase 1 and to the end of the round in phase 2.
{
"game_slug": "door-rush",
"server_time": "2026-07-04T15:41:43+00:00",
"current_round": 42,
"current_round_started_at": "2026-07-04T15:41:00+00:00",
"next_round_at": "2026-07-04T15:42:00+00:00",
"seconds_to_next_round": 17,
"game_state": {
"phase": "phase_2",
"seconds_to_next_phase": 17,
"rooms": [
{"capacity": 2, "reward": 247},
{"capacity": 5, "reward": 178},
{"capacity": 1, "reward": 276},
{"capacity": 3, "reward": 230},
{"capacity": 7, "reward": 123},
{"capacity": 10, "reward": 42}
],
"rush_bonus": 1.7
}
}
Before the problem starts, status returns current_round = 0. In game_state, the phase is
not_started, rooms is empty, and rush_bonus is null. No first-round parameters are
revealed before its start.
Both phases use the same authenticated move endpoint. In phase 1, submit:
POST /api/v1/games/door-rush/move
Authorization: Bearer <your-api-token>
Content-Type: application/json
{"entrance": 3, "mode": "flex"}
A successful phase 1 response is:
{
"status": "accepted",
"game_slug": "door-rush",
"round_number": 42,
"phase": "phase_1",
"next_phase_at": "2026-07-04T15:41:30+00:00",
"accepted_entrance": 3,
"accepted_mode": "flex",
"accepted_room": null
}
During phase 2, an authenticated flex team may request counts and submit its room:
GET /api/v1/games/door-rush/entrance-counts
Authorization: Bearer <your-api-token>
The response contains one count for every room. It includes both rush and flex entrances:
{
"entrance_counts": [4, 2, 7, 5, 6, 3]
}
POST /api/v1/games/door-rush/move
Authorization: Bearer <your-api-token>
Content-Type: application/json
{"room": 5}
A successful phase-2 response is:
{
"status": "accepted",
"game_slug": "door-rush",
"round_number": 42,
"phase": "phase_2",
"next_phase_at": "2026-07-04T15:42:00+00:00",
"accepted_entrance": null,
"accepted_mode": null,
"accepted_room": 5
}
The server determines the active phase from its clock and accepts only the corresponding payload.
Inspect the Latest Finalized Round
GET /api/v1/games/door-rush/last-round
Authorization: Bearer <your-api-token>
This endpoint returns the latest finalized round that had at least one submission.
entrance_counts contains the number of teams that first entered each room, final_counts
contains the number of teams that ended in each room, and rush_counts contains the number of
rush teams locked into each room. No team identities or opponents' individual choices are
revealed.
your_move is null if your team did not submit a valid phase-1 move. Otherwise,
reward_score is the direct reward before round weighting, and raw_score is
the weighted score added to your total.
{
"round": 1,
"rooms": [
{"capacity": 2, "reward": 247},
{"capacity": 5, "reward": 178},
{"capacity": 1, "reward": 276},
{"capacity": 3, "reward": 230},
{"capacity": 7, "reward": 123},
{"capacity": 10, "reward": 42}
],
"rush_bonus": 1.7,
"round_weight": 1.0,
"entrance_counts": [4, 2, 7, 5, 6, 3],
"final_counts": [2, 5, 1, 3, 7, 9],
"rush_counts": [1, 2, 1, 0, 3, 4],
"your_move": {
"entrance": 1,
"mode": "flex",
"final_room": 3,
"distance": 2,
"overcrowded": false,
"reward_score": 129.375,
"raw_score": 129.375
}
}
