From d40b4d15a55d46cc5cf73e5c5c73eaa3ffb803f5 Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 15 Jul 2025 13:56:41 +0200 Subject: [PATCH 1/6] docs: clean up formatting and mkdocs configuration - Remove empty line from index.md - Remove index.md from mkdocs.yml sections list --- docs/index.md | 1 - mkdocs.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 9faaa1e..cd59091 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,7 +36,6 @@ BalatroBot is a Python framework designed to help developers create automated bo [:octicons-arrow-right-24: Protocol API](protocol-api.md) - - :octicons-sparkle-fill-16:{ .lg .middle } __Documentation for LLM__ --- diff --git a/mkdocs.yml b/mkdocs.yml index 41deca8..bc1684e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -39,7 +39,6 @@ plugins: The project enables real-time bidirectional communication between the game and bot through TCP sockets. sections: Documentation: - - index.md - installation.md - developing-bots.md - balatrobot-api.md From e9814a741b22d3e3eccfc8109773f5e7744607da Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 15 Jul 2025 14:45:18 +0200 Subject: [PATCH 2/6] chore(dev): add CHANGELOG.md protection to Claude settings --- .claude/settings.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.claude/settings.json b/.claude/settings.json index 4f6a9fb..ce85533 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -6,7 +6,11 @@ "Bash(ruff format:*)", "Bash(basedpyright:*)" ], - "deny": [] + "deny": [ + "Edit(CHANGELOG.md)", + "MultiEdit(CHANGELOG.md)", + "Write(CHANGELOG.md)" + ] }, "hooks": { "PostToolUse": [ From f7df9905653d443b4653e1ec137f5ac225eeea45 Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 15 Jul 2025 15:02:26 +0200 Subject: [PATCH 3/6] refactor(log): rename params to arguments in logging system Rename function parameter from 'params' to 'arguments' across Lua logging system and test files to improve code consistency and clarity. Co-Authored-By: Claude --- src/lua/log.lua | 38 +++++++++--------- src/lua/types.lua | 2 +- tests/lua/test_runs.py | 4 +- tests/runs/one_ante_no_shop_1.jsonl | 34 ++++++++-------- tests/runs/one_ante_no_shop_2.jsonl | 62 ++++++++++++++--------------- 5 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/lua/log.lua b/src/lua/log.lua index b05a211..a1aed71 100644 --- a/src/lua/log.lua +++ b/src/lua/log.lua @@ -17,14 +17,14 @@ end ---Logs a function call to the JSONL file ---@param function_name string The name of the function being called ----@param params table The parameters passed to the function -function LOG.write(function_name, params) +---@param arguments table The parameters passed to the function +function LOG.write(function_name, arguments) ---@type LogEntry local log_entry = { timestamp_ms = math.floor(socket.gettime() * 1000), ["function"] = { name = function_name, - params = params, + arguments = arguments, }, -- game_state before the function call game_state = utils.get_game_state(), @@ -57,9 +57,9 @@ end function LOG.hook_go_to_menu() local original_function = G.FUNCS.go_to_menu G.FUNCS.go_to_menu = function(args) - local params = {} + local arguments = {} local name = "go_to_menu" - LOG.write(name, params) + LOG.write(name, arguments) return original_function(args) end sendDebugMessage("Hooked into G.FUNCS.go_to_menu for logging", "LOG") @@ -77,14 +77,14 @@ function LOG.hook_start_run() local timestamp = LOG.generate_iso8601_timestamp() LOG.current_run_file = LOG.mod_path .. "runs/" .. timestamp .. ".jsonl" sendInfoMessage("Starting new run log: " .. timestamp .. ".jsonl", "LOG") - local params = { + local arguments = { deck = G.GAME.selected_back.name, stake = args.stake, seed = args.seed, challenge = args.challenge and args.challenge.name, } local name = "start_run" - LOG.write(name, params) + LOG.write(name, arguments) return original_function(game_state, args) end sendDebugMessage("Hooked into G.FUNCS.start_run for logging", "LOG") @@ -98,9 +98,9 @@ end function LOG.hook_select_blind() local original_function = G.FUNCS.select_blind G.FUNCS.select_blind = function(args) - local params = { action = "select" } + local arguments = { action = "select" } local name = "skip_or_select_blind" - LOG.write(name, params) + LOG.write(name, arguments) return original_function(args) end sendDebugMessage("Hooked into G.FUNCS.select_blind for logging", "LOG") @@ -110,9 +110,9 @@ end function LOG.hook_skip_blind() local original_function = G.FUNCS.skip_blind G.FUNCS.skip_blind = function(args) - local params = { action = "skip" } + local arguments = { action = "skip" } local name = "skip_or_select_blind" - LOG.write(name, params) + LOG.write(name, arguments) return original_function(args) end sendDebugMessage("Hooked into G.FUNCS.skip_blind for logging", "LOG") @@ -132,9 +132,9 @@ function LOG.hook_play_cards_from_highlighted() table.insert(cards, i - 1) -- Adjust for 0-based indexing end end - local params = { action = "play_hand", cards = cards } + local arguments = { action = "play_hand", cards = cards } local name = "play_hand_or_discard" - LOG.write(name, params) + LOG.write(name, arguments) return original_function(args) end sendDebugMessage("Hooked into G.FUNCS.play_cards_from_highlighted for logging", "LOG") @@ -150,9 +150,9 @@ function LOG.hook_discard_cards_from_highlighted() table.insert(cards, i - 1) -- Adjust for 0-based indexing end end - local params = { action = "discard", cards = cards } + local arguments = { action = "discard", cards = cards } local name = "play_hand_or_discard" - LOG.write(name, params) + LOG.write(name, arguments) return original_function(args) end sendDebugMessage("Hooked into G.FUNCS.discard_cards_from_highlighted for logging", "LOG") @@ -166,9 +166,9 @@ end function LOG.hook_cash_out() local original_function = G.FUNCS.cash_out G.FUNCS.cash_out = function(args) - local params = {} + local arguments = {} local name = "cash_out" - LOG.write(name, params) + LOG.write(name, arguments) return original_function(args) end sendDebugMessage("Hooked into G.FUNCS.cash_out for logging", "LOG") @@ -182,9 +182,9 @@ end function LOG.hook_toggle_shop() local original_function = G.FUNCS.toggle_shop G.FUNCS.toggle_shop = function(args) - local params = { action = "next_round" } + local arguments = { action = "next_round" } local name = "shop" - LOG.write(name, params) + LOG.write(name, arguments) return original_function(args) end sendDebugMessage("Hooked into G.FUNCS.toggle_shop for logging", "LOG") diff --git a/src/lua/types.lua b/src/lua/types.lua index f2e09a0..d24f83d 100644 --- a/src/lua/types.lua +++ b/src/lua/types.lua @@ -123,7 +123,7 @@ ---@class LogEntry ---@field timestamp_ms number Timestamp in milliseconds since epoch ----@field function {name: string, params: table} Function call information +---@field function {name: string, arguments: table} Function call information ---@field game_state GameStateResponse Game state at time of logging -- ============================================================================= diff --git a/tests/lua/test_runs.py b/tests/lua/test_runs.py index a404850..5a5b0ec 100644 --- a/tests/lua/test_runs.py +++ b/tests/lua/test_runs.py @@ -53,7 +53,7 @@ def test_replay_run(self, tcp_client: socket.socket, jsonl_file: Path) -> None: # Call the API function with recorded parameters actual_game_state = send_and_receive_api_message( - tcp_client, function_call["name"], function_call["params"] + tcp_client, function_call["name"], function_call["arguments"] ) # Compare with the game_state from the next step (if it exists) @@ -64,7 +64,7 @@ def test_replay_run(self, tcp_client: socket.socket, jsonl_file: Path) -> None: # Assert complete game state equality assert actual_game_state == expected_game_state, ( f"Game state mismatch at step {step_num + 1} in {jsonl_file.name}\n" - f"Function: {function_call['name']}({function_call['params']})\n" + f"Function: {function_call['name']}({function_call['arguments']})\n" f"Expected: {expected_game_state}\n" f"Actual: {actual_game_state}" ) diff --git a/tests/runs/one_ante_no_shop_1.jsonl b/tests/runs/one_ante_no_shop_1.jsonl index 6d8d6a3..7e5e95a 100644 --- a/tests/runs/one_ante_no_shop_1.jsonl +++ b/tests/runs/one_ante_no_shop_1.jsonl @@ -1,17 +1,17 @@ -{"function":{"name":"start_run","params":{"stake":1,"deck":"Red Deck","seed":"OOOO155"}},"timestamp_ms":1752311853015,"game_state":{"hand":[{"config":{"card":{"value":"Ace","card_key":"S_A","name":"Ace of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"Jack","card_key":"C_J","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"2","card_key":"S_2","name":"2 of Spades","suit":"Spades"}},"label":"Base Card"}],"jokers":[],"state":4,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":543,"hands_played":5,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":2}}} -{"function":{"name":"skip_or_select_blind","params":{"action":"select"}},"timestamp_ms":1752311855035,"game_state":{"hand":[],"jokers":[],"state":7,"game":{"max_jokers":0,"skips":0,"blind_on_deck":"Small","discount_percent":0,"chips":0,"hands_played":0,"bankrupt_at":0,"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":0}}} -{"function":{"name":"play_hand_or_discard","params":{"action":"play_hand","cards":[0,1,2,3]}},"timestamp_ms":1752311857728,"game_state":{"hand":[{"config":{"card":{"value":"Jack","card_key":"S_J","name":"Jack of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"Jack","card_key":"H_J","name":"Jack of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"Jack","card_key":"C_J","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"Jack","card_key":"D_J","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"value":"10","card_key":"H_T","name":"10 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"10","card_key":"C_T","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"9","card_key":"C_9","name":"9 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"7","card_key":"D_7","name":"7 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":0,"blind_on_deck":"Small","discount_percent":0,"chips":0,"hands_played":0,"bankrupt_at":0,"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1}}} -{"function":{"name":"cash_out","params":[]},"timestamp_ms":1752311862305,"game_state":{"hand":[],"jokers":[],"state":8,"game":{"max_jokers":0,"skips":0,"blind_on_deck":"Small","discount_percent":0,"chips":700,"hands_played":1,"bankrupt_at":0,"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1}}} -{"function":{"name":"shop","params":{"action":"next_round"}},"timestamp_ms":1752311863670,"game_state":{"hand":[],"jokers":[],"state":5,"game":{"max_jokers":0,"skips":0,"blind_on_deck":"Small","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1}}} -{"function":{"name":"skip_or_select_blind","params":{"action":"skip"}},"timestamp_ms":1752311864683,"game_state":{"hand":[],"jokers":[],"state":7,"game":{"max_jokers":0,"skips":0,"blind_on_deck":"Big","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1}}} -{"function":{"name":"skip_or_select_blind","params":{"action":"select"}},"timestamp_ms":1752311865556,"game_state":{"hand":[],"jokers":[],"state":7,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1}}} -{"function":{"name":"play_hand_or_discard","params":{"action":"discard","cards":[2,3,5,6]}},"timestamp_ms":1752311881616,"game_state":{"hand":[{"config":{"card":{"value":"King","card_key":"S_K","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"H_K","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"Jack","card_key":"D_J","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"value":"7","card_key":"C_7","name":"7 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"C_5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"4","card_key":"S_4","name":"4 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"3","card_key":"D_3","name":"3 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2}}} -{"function":{"name":"play_hand_or_discard","params":{"action":"discard","cards":[2,3,6]}},"timestamp_ms":1752311888393,"game_state":{"hand":[{"config":{"card":{"value":"King","card_key":"S_K","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"H_K","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"9","card_key":"D_9","name":"9 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"value":"8","card_key":"C_8","name":"8 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"C_5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"2","card_key":"D_2","name":"2 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":3},"inflation":0,"round":2}}} -{"function":{"name":"play_hand_or_discard","params":{"action":"discard","cards":[3,5,6]}},"timestamp_ms":1752311903751,"game_state":{"hand":[{"config":{"card":{"value":"Ace","card_key":"H_A","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"S_K","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"H_K","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"8","card_key":"S_8","name":"8 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"C_5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"4","card_key":"C_4","name":"4 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":2},"inflation":0,"round":2}}} -{"function":{"name":"play_hand_or_discard","params":{"action":"discard","cards":[3,4,6]}},"timestamp_ms":1752311912011,"game_state":{"hand":[{"config":{"card":{"value":"Ace","card_key":"H_A","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"S_K","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"H_K","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"10","card_key":"S_T","name":"10 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"S_5","name":"5 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"4","card_key":"D_4","name":"4 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":2}}} -{"function":{"name":"play_hand_or_discard","params":{"action":"play_hand","cards":[1,2,3,4]}},"timestamp_ms":1752311920749,"game_state":{"hand":[{"config":{"card":{"value":"Ace","card_key":"H_A","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"S_K","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"H_K","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"C_K","name":"King of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"7","card_key":"S_7","name":"7 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"6","card_key":"H_6","name":"6 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":2}}} -{"function":{"name":"play_hand_or_discard","params":{"action":"play_hand","cards":[0,1,6]}},"timestamp_ms":1752311942014,"game_state":{"hand":[{"config":{"card":{"value":"Ace","card_key":"H_A","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"10","card_key":"C_T","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"6","card_key":"H_6","name":"6 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"4","card_key":"H_4","name":"4 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"3","card_key":"H_3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"2","card_key":"C_2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":180,"hands_played":2,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":2}}} -{"function":{"name":"play_hand_or_discard","params":{"action":"play_hand","cards":[2,3,4,5,6]}},"timestamp_ms":1752311948765,"game_state":{"hand":[{"config":{"card":{"value":"Queen","card_key":"S_Q","name":"Queen of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"Queen","card_key":"C_Q","name":"Queen of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"7","card_key":"H_7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"6","card_key":"H_6","name":"6 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"4","card_key":"H_4","name":"4 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"3","card_key":"H_3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":196,"hands_played":3,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":2}}} -{"function":{"name":"cash_out","params":[]},"timestamp_ms":1752311954275,"game_state":{"hand":[],"jokers":[],"state":8,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":1196,"hands_played":4,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":2}}} -{"function":{"name":"shop","params":{"action":"next_round"}},"timestamp_ms":1752311957874,"game_state":{"hand":[],"jokers":[],"state":5,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Small","discount_percent":0,"chips":0,"hands_played":4,"bankrupt_at":0,"dollars":30,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2}}} -{"function":{"name":"go_to_menu","params":[]},"timestamp_ms":1752311999281,"game_state":{"hand":[],"jokers":[],"state":7,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Small","discount_percent":0,"chips":0,"hands_played":4,"bankrupt_at":0,"dollars":30,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2}}} +{"function":{"name":"start_run","arguments":{"stake":1,"deck":"Red Deck","seed":"OOOO155"}},"timestamp_ms":1752311853015,"game_state":{"hand":[{"config":{"card":{"value":"Ace","card_key":"S_A","name":"Ace of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"Jack","card_key":"C_J","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"2","card_key":"S_2","name":"2 of Spades","suit":"Spades"}},"label":"Base Card"}],"jokers":[],"state":4,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":543,"hands_played":5,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":2}}} +{"function":{"name":"skip_or_select_blind","arguments":{"action":"select"}},"timestamp_ms":1752311855035,"game_state":{"hand":[],"jokers":[],"state":7,"game":{"max_jokers":0,"skips":0,"blind_on_deck":"Small","discount_percent":0,"chips":0,"hands_played":0,"bankrupt_at":0,"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":0}}} +{"function":{"name":"play_hand_or_discard","arguments":{"action":"play_hand","cards":[0,1,2,3]}},"timestamp_ms":1752311857728,"game_state":{"hand":[{"config":{"card":{"value":"Jack","card_key":"S_J","name":"Jack of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"Jack","card_key":"H_J","name":"Jack of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"Jack","card_key":"C_J","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"Jack","card_key":"D_J","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"value":"10","card_key":"H_T","name":"10 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"10","card_key":"C_T","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"9","card_key":"C_9","name":"9 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"7","card_key":"D_7","name":"7 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":0,"blind_on_deck":"Small","discount_percent":0,"chips":0,"hands_played":0,"bankrupt_at":0,"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1}}} +{"function":{"name":"cash_out","arguments":[]},"timestamp_ms":1752311862305,"game_state":{"hand":[],"jokers":[],"state":8,"game":{"max_jokers":0,"skips":0,"blind_on_deck":"Small","discount_percent":0,"chips":700,"hands_played":1,"bankrupt_at":0,"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1}}} +{"function":{"name":"shop","arguments":{"action":"next_round"}},"timestamp_ms":1752311863670,"game_state":{"hand":[],"jokers":[],"state":5,"game":{"max_jokers":0,"skips":0,"blind_on_deck":"Small","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1}}} +{"function":{"name":"skip_or_select_blind","arguments":{"action":"skip"}},"timestamp_ms":1752311864683,"game_state":{"hand":[],"jokers":[],"state":7,"game":{"max_jokers":0,"skips":0,"blind_on_deck":"Big","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1}}} +{"function":{"name":"skip_or_select_blind","arguments":{"action":"select"}},"timestamp_ms":1752311865556,"game_state":{"hand":[],"jokers":[],"state":7,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1}}} +{"function":{"name":"play_hand_or_discard","arguments":{"action":"discard","cards":[2,3,5,6]}},"timestamp_ms":1752311881616,"game_state":{"hand":[{"config":{"card":{"value":"King","card_key":"S_K","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"H_K","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"Jack","card_key":"D_J","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"value":"7","card_key":"C_7","name":"7 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"C_5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"4","card_key":"S_4","name":"4 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"3","card_key":"D_3","name":"3 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2}}} +{"function":{"name":"play_hand_or_discard","arguments":{"action":"discard","cards":[2,3,6]}},"timestamp_ms":1752311888393,"game_state":{"hand":[{"config":{"card":{"value":"King","card_key":"S_K","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"H_K","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"9","card_key":"D_9","name":"9 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"value":"8","card_key":"C_8","name":"8 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"C_5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"2","card_key":"D_2","name":"2 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":3},"inflation":0,"round":2}}} +{"function":{"name":"play_hand_or_discard","arguments":{"action":"discard","cards":[3,5,6]}},"timestamp_ms":1752311903751,"game_state":{"hand":[{"config":{"card":{"value":"Ace","card_key":"H_A","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"S_K","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"H_K","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"8","card_key":"S_8","name":"8 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"C_5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"4","card_key":"C_4","name":"4 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":2},"inflation":0,"round":2}}} +{"function":{"name":"play_hand_or_discard","arguments":{"action":"discard","cards":[3,4,6]}},"timestamp_ms":1752311912011,"game_state":{"hand":[{"config":{"card":{"value":"Ace","card_key":"H_A","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"S_K","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"H_K","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"10","card_key":"S_T","name":"10 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"S_5","name":"5 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"4","card_key":"D_4","name":"4 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":2}}} +{"function":{"name":"play_hand_or_discard","arguments":{"action":"play_hand","cards":[1,2,3,4]}},"timestamp_ms":1752311920749,"game_state":{"hand":[{"config":{"card":{"value":"Ace","card_key":"H_A","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"S_K","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"H_K","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"King","card_key":"C_K","name":"King of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"7","card_key":"S_7","name":"7 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"6","card_key":"H_6","name":"6 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":0,"hands_played":1,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":2}}} +{"function":{"name":"play_hand_or_discard","arguments":{"action":"play_hand","cards":[0,1,6]}},"timestamp_ms":1752311942014,"game_state":{"hand":[{"config":{"card":{"value":"Ace","card_key":"H_A","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"10","card_key":"C_T","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"6","card_key":"H_6","name":"6 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"4","card_key":"H_4","name":"4 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"3","card_key":"H_3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"2","card_key":"C_2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":180,"hands_played":2,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":2}}} +{"function":{"name":"play_hand_or_discard","arguments":{"action":"play_hand","cards":[2,3,4,5,6]}},"timestamp_ms":1752311948765,"game_state":{"hand":[{"config":{"card":{"value":"Queen","card_key":"S_Q","name":"Queen of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"value":"Queen","card_key":"C_Q","name":"Queen of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"value":"7","card_key":"H_7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"6","card_key":"H_6","name":"6 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"5","card_key":"H_5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"4","card_key":"H_4","name":"4 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"value":"3","card_key":"H_3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"jokers":[],"state":1,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":196,"hands_played":3,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":2}}} +{"function":{"name":"cash_out","arguments":[]},"timestamp_ms":1752311954275,"game_state":{"hand":[],"jokers":[],"state":8,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Boss","discount_percent":0,"chips":1196,"hands_played":4,"bankrupt_at":0,"dollars":20,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":2}}} +{"function":{"name":"shop","arguments":{"action":"next_round"}},"timestamp_ms":1752311957874,"game_state":{"hand":[],"jokers":[],"state":5,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Small","discount_percent":0,"chips":0,"hands_played":4,"bankrupt_at":0,"dollars":30,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2}}} +{"function":{"name":"go_to_menu","arguments":[]},"timestamp_ms":1752311999281,"game_state":{"hand":[],"jokers":[],"state":7,"game":{"max_jokers":0,"skips":1,"blind_on_deck":"Small","discount_percent":0,"chips":0,"hands_played":4,"bankrupt_at":0,"dollars":30,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2}}} diff --git a/tests/runs/one_ante_no_shop_2.jsonl b/tests/runs/one_ante_no_shop_2.jsonl index 11501dd..511a98a 100644 --- a/tests/runs/one_ante_no_shop_2.jsonl +++ b/tests/runs/one_ante_no_shop_2.jsonl @@ -1,31 +1,31 @@ -{"function":{"name":"start_run","params":{"stake":1,"deck":"Red Deck","seed":"OOOOOOO"}},"game_state":{"game":{"dollars":0,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":0,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":0,"chips":0,"discount_percent":0},"jokers":[],"state":11},"timestamp_ms":1752317267376} -{"function":{"name":"skip_or_select_blind","params":{"action":"skip"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":0,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":0,"chips":0,"blind_on_deck":"Small","discount_percent":0},"jokers":[],"hand":[],"state":7},"timestamp_ms":1752317270052} -{"function":{"name":"skip_or_select_blind","params":{"action":"skip"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":0,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":1,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[],"hand":[],"state":7},"timestamp_ms":1752317270894} -{"function":{"name":"skip_or_select_blind","params":{"action":"select"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":0,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[],"state":7},"timestamp_ms":1752317274530} -{"function":{"name":"play_hand_or_discard","params":{"cards":[1,5,6,7],"action":"discard"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"D_A","value":"Ace","name":"Ace of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_K","value":"King","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_K","value":"King","name":"King of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_J","value":"Jack","name":"Jack of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_9","value":"9","name":"9 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_7","value":"7","name":"7 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_3","value":"3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317292760} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,2,5,6],"action":"discard"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":3},"inflation":0,"round":1,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"D_A","value":"Ace","name":"Ace of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_K","value":"King","name":"King of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_J","value":"Jack","name":"Jack of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_8","value":"8","name":"8 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_4","value":"4","name":"4 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_2","value":"2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317300719} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,3,4,6,7],"action":"play_hand"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":2},"inflation":0,"round":1,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"C_K","value":"King","name":"King of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_9","value":"9","name":"9 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_8","value":"8","name":"8 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_6","value":"6","name":"6 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_5","value":"5","name":"5 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_4","value":"4","name":"4 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_2","value":"2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317323546} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,3,4,5,6],"action":"discard"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":2},"inflation":0,"round":1,"hands_played":1,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":260,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"H_J","value":"Jack","name":"Jack of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_9","value":"9","name":"9 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_9","value":"9","name":"9 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_5","value":"5","name":"5 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_4","value":"4","name":"4 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_3","value":"3","name":"3 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317330725} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,1,3,5,7],"action":"discard"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":1,"hands_played":1,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":260,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"H_A","value":"Ace","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_Q","value":"Queen","name":"Queen of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_5","value":"5","name":"5 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_3","value":"3","name":"3 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_2","value":"2","name":"2 of Spades","suit":"Spades"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317338876} -{"function":{"name":"play_hand_or_discard","params":{"cards":[1,2,3,4,5],"action":"play_hand"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":1,"hands_played":1,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":260,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_T","value":"10","name":"10 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_6","value":"6","name":"6 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_6","value":"6","name":"6 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_5","value":"5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_3","value":"3","name":"3 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317347502} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,2,4,5,7],"action":"play_hand"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":1,"hands_played":2,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":588,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"C_A","value":"Ace","name":"Ace of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_Q","value":"Queen","name":"Queen of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_8","value":"8","name":"8 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_7","value":"7","name":"7 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_5","value":"5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_4","value":"4","name":"4 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_3","value":"3","name":"3 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317353840} -{"function":{"name":"cash_out","params":[]},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":1,"hands_played":3,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":872,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[],"state":8},"timestamp_ms":1752317359279} -{"function":{"name":"shop","params":{"action":"next_round"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1,"hands_played":3,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Small","discount_percent":0},"jokers":[],"hand":[],"state":5},"timestamp_ms":1752317365251} -{"function":{"name":"skip_or_select_blind","params":{"action":"skip"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1,"hands_played":3,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Small","discount_percent":0},"jokers":[],"hand":[],"state":7},"timestamp_ms":1752317374892} -{"function":{"name":"skip_or_select_blind","params":{"action":"select"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1,"hands_played":3,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[],"state":7},"timestamp_ms":1752317395794} -{"function":{"name":"play_hand_or_discard","params":{"cards":[2,4,6,7],"action":"discard"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2,"hands_played":3,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"D_K","value":"King","name":"King of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_T","value":"10","name":"10 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_8","value":"8","name":"8 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_7","value":"7","name":"7 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_5","value":"5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317417884} -{"function":{"name":"play_hand_or_discard","params":{"cards":[5,6,7],"action":"discard"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":3},"inflation":0,"round":2,"hands_played":3,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_A","value":"Ace","name":"Ace of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_K","value":"King","name":"King of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_4","value":"4","name":"4 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_3","value":"3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317426062} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,1,4,7],"action":"discard"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":2},"inflation":0,"round":2,"hands_played":3,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_A","value":"Ace","name":"Ace of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_A","value":"Ace","name":"Ace of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_K","value":"King","name":"King of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_T","value":"10","name":"10 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_7","value":"7","name":"7 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317444718} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,2,3,4,5],"action":"play_hand"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":2,"hands_played":3,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"D_K","value":"King","name":"King of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_Q","value":"Queen","name":"Queen of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_6","value":"6","name":"6 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_2","value":"2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317457398} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,1,4,5],"action":"play_hand"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":2,"hands_played":4,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":632,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_K","value":"King","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_K","value":"King","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_Q","value":"Queen","name":"Queen of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_Q","value":"Queen","name":"Queen of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_J","value":"Jack","name":"Jack of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_2","value":"2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317471886} -{"function":{"name":"cash_out","params":[]},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":2,"hands_played":5,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":1352,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[],"state":8},"timestamp_ms":1752317476935} -{"function":{"name":"shop","params":{"action":"next_round"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2,"hands_played":5,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[],"state":5},"timestamp_ms":1752317479970} -{"function":{"name":"skip_or_select_blind","params":{"action":"select"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2,"hands_played":5,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[],"state":7},"timestamp_ms":1752317484732} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,1,2,3,6],"action":"discard"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":3,"hands_played":5,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_J","value":"Jack","name":"Jack of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_T","value":"10","name":"10 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_8","value":"8","name":"8 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_5","value":"5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_3","value":"3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317498661} -{"function":{"name":"play_hand_or_discard","params":{"cards":[4,5,6,7],"action":"play_hand"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":3},"inflation":0,"round":3,"hands_played":5,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"D_Q","value":"Queen","name":"Queen of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_9","value":"9","name":"9 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_3","value":"3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_3","value":"3","name":"3 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_2","value":"2","name":"2 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_2","value":"2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317511429} -{"function":{"name":"play_hand_or_discard","params":{"cards":[1,2,3,4,7],"action":"discard"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":3},"inflation":0,"round":3,"hands_played":6,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":250,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"H_A","value":"Ace","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_A","value":"Ace","name":"Ace of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_Q","value":"Queen","name":"Queen of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_9","value":"9","name":"9 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_4","value":"4","name":"4 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317520839} -{"function":{"name":"play_hand_or_discard","params":{"cards":[2,3,7],"action":"discard"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":2},"inflation":0,"round":3,"hands_played":6,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":250,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"H_A","value":"Ace","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_A","value":"Ace","name":"Ace of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_K","value":"King","name":"King of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_8","value":"8","name":"8 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_8","value":"8","name":"8 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_3","value":"3","name":"3 of Spades","suit":"Spades"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317526760} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,1,2,3],"action":"play_hand"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":3,"hands_played":6,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":250,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_A","value":"Ace","name":"Ace of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_A","value":"Ace","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_A","value":"Ace","name":"Ace of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_K","value":"King","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_8","value":"8","name":"8 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_6","value":"6","name":"6 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317541870} -{"function":{"name":"play_hand_or_discard","params":{"cards":[0,1,2,7],"action":"play_hand"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":3,"hands_played":7,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":822,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_Q","value":"Queen","name":"Queen of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_8","value":"8","name":"8 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_6","value":"6","name":"6 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_2","value":"2","name":"2 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317557022} -{"function":{"name":"play_hand_or_discard","params":{"cards":[1,3,4,5,6],"action":"play_hand"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":3,"hands_played":8,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":1062,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"H_T","value":"10","name":"10 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_8","value":"8","name":"8 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_6","value":"6","name":"6 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_4","value":"4","name":"4 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_3","value":"3","name":"3 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317574719} -{"function":{"name":"go_to_menu","params":[]},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":3,"hands_played":9,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":1262,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"H_T","value":"10","name":"10 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_3","value":"3","name":"3 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"state":4},"timestamp_ms":1752317582918} +{"function":{"name":"start_run","arguments":{"stake":1,"deck":"Red Deck","seed":"OOOOOOO"}},"game_state":{"game":{"dollars":0,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":0,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":0,"chips":0,"discount_percent":0},"jokers":[],"state":11},"timestamp_ms":1752317267376} +{"function":{"name":"skip_or_select_blind","arguments":{"action":"skip"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":0,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":0,"chips":0,"blind_on_deck":"Small","discount_percent":0},"jokers":[],"hand":[],"state":7},"timestamp_ms":1752317270052} +{"function":{"name":"skip_or_select_blind","arguments":{"action":"skip"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":0,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":1,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[],"hand":[],"state":7},"timestamp_ms":1752317270894} +{"function":{"name":"skip_or_select_blind","arguments":{"action":"select"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":0,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[],"state":7},"timestamp_ms":1752317274530} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[1,5,6,7],"action":"discard"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"D_A","value":"Ace","name":"Ace of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_K","value":"King","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_K","value":"King","name":"King of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_J","value":"Jack","name":"Jack of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_9","value":"9","name":"9 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_7","value":"7","name":"7 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_3","value":"3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317292760} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,2,5,6],"action":"discard"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":3},"inflation":0,"round":1,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"D_A","value":"Ace","name":"Ace of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_K","value":"King","name":"King of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_J","value":"Jack","name":"Jack of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_8","value":"8","name":"8 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_4","value":"4","name":"4 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_2","value":"2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317300719} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,3,4,6,7],"action":"play_hand"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":2},"inflation":0,"round":1,"hands_played":0,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"C_K","value":"King","name":"King of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_9","value":"9","name":"9 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_8","value":"8","name":"8 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_6","value":"6","name":"6 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_5","value":"5","name":"5 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_4","value":"4","name":"4 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_2","value":"2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317323546} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,3,4,5,6],"action":"discard"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":2},"inflation":0,"round":1,"hands_played":1,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":260,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"H_J","value":"Jack","name":"Jack of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_9","value":"9","name":"9 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_9","value":"9","name":"9 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_5","value":"5","name":"5 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_4","value":"4","name":"4 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_3","value":"3","name":"3 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317330725} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,1,3,5,7],"action":"discard"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":1,"hands_played":1,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":260,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"H_A","value":"Ace","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_Q","value":"Queen","name":"Queen of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_5","value":"5","name":"5 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_3","value":"3","name":"3 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_2","value":"2","name":"2 of Spades","suit":"Spades"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317338876} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[1,2,3,4,5],"action":"play_hand"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":1,"hands_played":1,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":260,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_T","value":"10","name":"10 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_6","value":"6","name":"6 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_6","value":"6","name":"6 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_5","value":"5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_3","value":"3","name":"3 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317347502} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,2,4,5,7],"action":"play_hand"}},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":1,"hands_played":2,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":588,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[{"config":{"card":{"card_key":"C_A","value":"Ace","name":"Ace of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_Q","value":"Queen","name":"Queen of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_8","value":"8","name":"8 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_7","value":"7","name":"7 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_5","value":"5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_4","value":"4","name":"4 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_3","value":"3","name":"3 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317353840} +{"function":{"name":"cash_out","arguments":[]},"game_state":{"game":{"dollars":4,"interest_cap":25,"current_round":{"discards_left":0},"inflation":0,"round":1,"hands_played":3,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":872,"blind_on_deck":"Boss","discount_percent":0},"jokers":[],"hand":[],"state":8},"timestamp_ms":1752317359279} +{"function":{"name":"shop","arguments":{"action":"next_round"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1,"hands_played":3,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Small","discount_percent":0},"jokers":[],"hand":[],"state":5},"timestamp_ms":1752317365251} +{"function":{"name":"skip_or_select_blind","arguments":{"action":"skip"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1,"hands_played":3,"max_jokers":0,"bankrupt_at":0,"skips":2,"chips":0,"blind_on_deck":"Small","discount_percent":0},"jokers":[],"hand":[],"state":7},"timestamp_ms":1752317374892} +{"function":{"name":"skip_or_select_blind","arguments":{"action":"select"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":1,"hands_played":3,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[],"state":7},"timestamp_ms":1752317395794} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[2,4,6,7],"action":"discard"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2,"hands_played":3,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"D_K","value":"King","name":"King of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_T","value":"10","name":"10 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_8","value":"8","name":"8 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_7","value":"7","name":"7 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_5","value":"5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317417884} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[5,6,7],"action":"discard"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":3},"inflation":0,"round":2,"hands_played":3,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_A","value":"Ace","name":"Ace of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_K","value":"King","name":"King of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_4","value":"4","name":"4 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_3","value":"3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317426062} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,1,4,7],"action":"discard"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":2},"inflation":0,"round":2,"hands_played":3,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_A","value":"Ace","name":"Ace of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_A","value":"Ace","name":"Ace of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_K","value":"King","name":"King of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_T","value":"10","name":"10 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_7","value":"7","name":"7 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317444718} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,2,3,4,5],"action":"play_hand"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":2,"hands_played":3,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"D_K","value":"King","name":"King of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_Q","value":"Queen","name":"Queen of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_6","value":"6","name":"6 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_2","value":"2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317457398} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,1,4,5],"action":"play_hand"}},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":2,"hands_played":4,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":632,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_K","value":"King","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_K","value":"King","name":"King of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_Q","value":"Queen","name":"Queen of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_Q","value":"Queen","name":"Queen of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_J","value":"Jack","name":"Jack of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_2","value":"2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317471886} +{"function":{"name":"cash_out","arguments":[]},"game_state":{"game":{"dollars":10,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":2,"hands_played":5,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":1352,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[],"state":8},"timestamp_ms":1752317476935} +{"function":{"name":"shop","arguments":{"action":"next_round"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2,"hands_played":5,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Big","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[],"state":5},"timestamp_ms":1752317479970} +{"function":{"name":"skip_or_select_blind","arguments":{"action":"select"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":2,"hands_played":5,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[],"state":7},"timestamp_ms":1752317484732} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,1,2,3,6],"action":"discard"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":4},"inflation":0,"round":3,"hands_played":5,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_J","value":"Jack","name":"Jack of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_T","value":"10","name":"10 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_T","value":"10","name":"10 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_8","value":"8","name":"8 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_5","value":"5","name":"5 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_3","value":"3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317498661} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[4,5,6,7],"action":"play_hand"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":3},"inflation":0,"round":3,"hands_played":5,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":0,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"D_Q","value":"Queen","name":"Queen of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_9","value":"9","name":"9 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_3","value":"3","name":"3 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_3","value":"3","name":"3 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_2","value":"2","name":"2 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_2","value":"2","name":"2 of Clubs","suit":"Clubs"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317511429} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[1,2,3,4,7],"action":"discard"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":3},"inflation":0,"round":3,"hands_played":6,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":250,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"H_A","value":"Ace","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_A","value":"Ace","name":"Ace of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_Q","value":"Queen","name":"Queen of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_T","value":"10","name":"10 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_9","value":"9","name":"9 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_4","value":"4","name":"4 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317520839} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[2,3,7],"action":"discard"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":2},"inflation":0,"round":3,"hands_played":6,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":250,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"H_A","value":"Ace","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_A","value":"Ace","name":"Ace of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_K","value":"King","name":"King of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_8","value":"8","name":"8 of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_8","value":"8","name":"8 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_3","value":"3","name":"3 of Spades","suit":"Spades"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317526760} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,1,2,3],"action":"play_hand"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":3,"hands_played":6,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":250,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_A","value":"Ace","name":"Ace of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_A","value":"Ace","name":"Ace of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_A","value":"Ace","name":"Ace of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"S_K","value":"King","name":"King of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_8","value":"8","name":"8 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_6","value":"6","name":"6 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317541870} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[0,1,2,7],"action":"play_hand"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":3,"hands_played":7,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":822,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"S_Q","value":"Queen","name":"Queen of Spades","suit":"Spades"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_J","value":"Jack","name":"Jack of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_J","value":"Jack","name":"Jack of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_8","value":"8","name":"8 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_6","value":"6","name":"6 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_2","value":"2","name":"2 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317557022} +{"function":{"name":"play_hand_or_discard","arguments":{"cards":[1,3,4,5,6],"action":"play_hand"}},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":3,"hands_played":8,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":1062,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"H_T","value":"10","name":"10 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_8","value":"8","name":"8 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_7","value":"7","name":"7 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_6","value":"6","name":"6 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"H_5","value":"5","name":"5 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"C_4","value":"4","name":"4 of Clubs","suit":"Clubs"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_3","value":"3","name":"3 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"state":1},"timestamp_ms":1752317574719} +{"function":{"name":"go_to_menu","arguments":[]},"game_state":{"game":{"dollars":18,"interest_cap":25,"current_round":{"discards_left":1},"inflation":0,"round":3,"hands_played":9,"max_jokers":2,"bankrupt_at":0,"skips":3,"chips":1262,"blind_on_deck":"Boss","discount_percent":0},"jokers":[{"config":{"center":{"rarity":1,"cost":3,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":0,"x":2},"effect":"Type Mult","name":"Jolly Joker","cost_mult":1,"key":"j_jolly","order":6,"_discovered_unlocked_overwritten":true,"_u":true,"_saved_d_u":true,"config":{"t_mult":8,"type":"Pair"},"perishable_compat":true,"eternal_compat":true,"blueprint_compat":true,"set":"Joker","_d":false}},"label":"Jolly Joker"},{"config":{"center":{"rarity":1,"cost":5,"unlocked":true,"discovered":true,"alerted":true,"pos":{"y":13,"x":2},"name":"Photograph","key":"j_photograph","order":78,"_discovered_unlocked_overwritten":true,"_u":true,"config":{"extra":2},"_saved_d_u":true,"perishable_compat":true,"eternal_compat":true,"set":"Joker","blueprint_compat":true,"_d":false}},"label":"Photograph"}],"hand":[{"config":{"card":{"card_key":"H_T","value":"10","name":"10 of Hearts","suit":"Hearts"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_8","value":"8","name":"8 of Diamonds","suit":"Diamonds"}},"label":"Base Card"},{"config":{"card":{"card_key":"D_3","value":"3","name":"3 of Diamonds","suit":"Diamonds"}},"label":"Base Card"}],"state":4},"timestamp_ms":1752317582918} From d7684c96f2dd8b5646bf26556b23a17207f660ad Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 15 Jul 2025 15:18:48 +0200 Subject: [PATCH 4/6] feat(log): add logging to BalatroClient connection and API calls - Add logging import and logger initialization - Log connection attempts and successful connections - Log API request names and completion status - Log errors for connection failures and API errors - Log disconnection events Co-Authored-By: Claude --- src/balatrobot/client.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/balatrobot/client.py b/src/balatrobot/client.py index b48174a..3569851 100644 --- a/src/balatrobot/client.py +++ b/src/balatrobot/client.py @@ -1,6 +1,7 @@ """Main BalatroBot client for communicating with the game.""" import json +import logging import socket from typing import Any, Literal, Self @@ -18,6 +19,8 @@ StartRunRequest, ) +logger = logging.getLogger(__name__) + class BalatroClient: """Client for communicating with the BalatroBot game API.""" @@ -58,6 +61,7 @@ def connect(self) -> None: if self._connected: return + logger.info(f"Connecting to BalatroBot API at {self.host}:{self.port}") try: self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.settimeout(self.timeout) @@ -66,7 +70,11 @@ def connect(self) -> None: ) self._socket.connect((self.host, self.port)) self._connected = True + logger.info( + f"Successfully connected to BalatroBot API at {self.host}:{self.port}" + ) except (socket.error, OSError) as e: + logger.error(f"Failed to connect to {self.host}:{self.port}: {e}") raise ConnectionFailedError( f"Failed to connect to {self.host}:{self.port}", error_code="E008", @@ -76,6 +84,7 @@ def connect(self) -> None: def disconnect(self) -> None: """Disconnect from the BalatroBot game API.""" if self._socket: + logger.info(f"Disconnecting from BalatroBot API at {self.host}:{self.port}") self._socket.close() self._socket = None self._connected = False @@ -106,6 +115,7 @@ def _send_request(self, name: str, arguments: dict[str, Any]) -> dict[str, Any]: # Create and validate request request = APIRequest(name=name, arguments=arguments) + logger.info(f"Sending API request: {name}") try: # Send request @@ -118,17 +128,21 @@ def _send_request(self, name: str, arguments: dict[str, Any]) -> dict[str, Any]: # Check for error response if "error" in response_data: + logger.error(f"API request {name} failed: {response_data.get('error')}") raise create_exception_from_error_response(response_data) + logger.info(f"API request {name} completed successfully") return response_data except socket.error as e: + logger.error(f"Socket error during API request {name}: {e}") raise ConnectionFailedError( f"Socket error during communication: {e}", error_code="E008", context={"error": str(e)}, ) from e except json.JSONDecodeError as e: + logger.error(f"Invalid JSON response from API request {name}: {e}") raise BalatroError( f"Invalid JSON response from game: {e}", error_code="E001", From 86bcda27df0d1ed5bc2a2455d3aa9cf77e80c7eb Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 15 Jul 2025 15:19:59 +0200 Subject: [PATCH 5/6] docs: add comprehensive logging systems documentation - Add logging-systems.md covering JSONL run logging, Python SDK logging, and mod logging - Document JSONL format specification for run replay functionality - Include Python SDK logging configuration examples - Add logging systems page to mkdocs navigation Co-Authored-By: Claude --- docs/logging-systems.md | 119 ++++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 120 insertions(+) create mode 100644 docs/logging-systems.md diff --git a/docs/logging-systems.md b/docs/logging-systems.md new file mode 100644 index 0000000..69d331e --- /dev/null +++ b/docs/logging-systems.md @@ -0,0 +1,119 @@ +# Logging Systems + +BalatroBot implements three distinct logging systems to support different aspects of development, debugging, and analysis: + +1. [**JSONL Run Logging**](#jsonl-run-logging) - Records complete game runs for replay and analysis +2. [**Python SDK Logging**](#python-sdk-logging) - Future logging capabilities for the Python framework +3. [**Mod Logging**](#mod-logging) - Traditional streamodded logging for mod development and debugging + +## JSONL Run Logging + +The run logging system records complete game runs as JSONL (JSON Lines) files. Each line represents a single game action with its parameters, timestamp, and game state **before** the action. + +The system hooks into these game functions: + +- `start_run`: begins a new game run +- `skip_or_select_blind`: blind selection actions +- `play_hand_or_discard`: card play actions +- `cash_out`: end blind and collect rewards +- `shop`: shop interactions +- `go_to_menu`: return to main menu + +The JSONL files are automatically created when: + +- **Playing manually**: Starting a new run through the game interface +- **Using the API**: Interacting with the game through the TCP API + +Files are saved as: `{mod_path}/runs/YYYYMMDDTHHMMSS.jsonl` + +!!! tip "Replay runs" + + The JSONL logs enable complete run replay for testing and analysis. + + ```python + state = load_jsonl_run("20250714T145700.jsonl") + for step in state: + send_and_receive_api_message( + tcp_client, + step["function"]["name"], + step["function"]["arguments"] + ) + ``` + +Examples for runs can be found in the [test suite](https://github.com/S1M0N38/balatrobot/tree/main/tests/runs). + +### Format Specification + +Each log entry follows this structure: + +```json +{ + "timestamp_ms": int, + "function": { + "name": "...", + "arguments": {...} + }, + "game_state": { ... } +} +``` + +- **`timestamp_ms`**: Unix timestamp in milliseconds when the action occurred +- **`function`**: The game function that was called + - `name`: Function name (e.g., "start_run", "play_hand_or_discard", "cash_out") + - `arguments`: Arguments passed to the function +- **`game_state`**: Complete game state **before** the function execution + +## Python SDK Logging + +The Python SDK (`src/balatrobot/`) implements structured logging for bot development and debugging. The logging system provides visibility into client operations, API communications, and error handling. + +### What Gets Logged + +The `BalatroClient` logs the following operations: + +- **Connection events**: When connecting to and disconnecting from the game API +- **API requests**: Function names being called and their completion status +- **Errors**: Connection failures, socket errors, and invalid API responses + +### Configuration Example + +The SDK uses Python's built-in `logging` module. Configure it in your bot code before using the client: + +```python +import logging +from balatrobot import BalatroClient + +# Configure logging +log_format = '%(asctime)s [%(levelname)s] %(name)s: %(message)s' +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.INFO) +file_handler = logging.FileHandler('balatrobot.log') +file_handler.setLevel(logging.DEBUG) + +logging.basicConfig( + level=logging.DEBUG, + format=log_format, + handlers=[console_handler, file_handler] +) + +# Use the client +with BalatroClient() as client: + state = client.get_game_state() + client.start_run(deck="Red Deck", stake=1) +``` + +## Mod Logging + +BalatroBot uses Steamodded's built-in logging system for mod development and debugging. + +- **Traditional logging**: Standard log levels (DEBUG, INFO, WARNING, ERROR) +- **Development focus**: Primarily for debugging mod functionality +- **Console output**: Displays in game console and log files + +```lua +-- Available through Steamodded +sendDebugMessage("This is a debug message") +sendInfoMessage("This is an info message") +sendWarningMessage("This is a warning message") +sendErrorMessage("This is an error message") +``` diff --git a/mkdocs.yml b/mkdocs.yml index bc1684e..5b58a85 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -61,6 +61,7 @@ nav: - Developing Bots: developing-bots.md - BalatroBot API: balatrobot-api.md - Protocol API: protocol-api.md + - Logging Systems: logging-systems.md markdown_extensions: - toc: toc_depth: 3 From bb5ba49bf3a29800e411a486bf642d304f9d5a2e Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 15 Jul 2025 15:30:12 +0200 Subject: [PATCH 6/6] refactor(log): reduce verbosity of API request logging Changes log level from info to debug for API request start and completion messages to reduce noise in standard output while maintaining detailed debugging capability. Co-Authored-By: Claude --- src/balatrobot/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/balatrobot/client.py b/src/balatrobot/client.py index 3569851..5003dc3 100644 --- a/src/balatrobot/client.py +++ b/src/balatrobot/client.py @@ -115,7 +115,7 @@ def _send_request(self, name: str, arguments: dict[str, Any]) -> dict[str, Any]: # Create and validate request request = APIRequest(name=name, arguments=arguments) - logger.info(f"Sending API request: {name}") + logger.debug(f"Sending API request: {name}") try: # Send request @@ -131,7 +131,7 @@ def _send_request(self, name: str, arguments: dict[str, Any]) -> dict[str, Any]: logger.error(f"API request {name} failed: {response_data.get('error')}") raise create_exception_from_error_response(response_data) - logger.info(f"API request {name} completed successfully") + logger.debug(f"API request {name} completed successfully") return response_data except socket.error as e: