Skip to content

feat!: balatrobot v2#155

Open
S1M0N38 wants to merge 10 commits intomainfrom
dev
Open

feat!: balatrobot v2#155
S1M0N38 wants to merge 10 commits intomainfrom
dev

Conversation

Previously, used_vouchers extracted descriptions from static
voucher_data.description which was unreliable. Now uses
get_voucher_effect() that fetches effect text via the game's
localize() function with proper loc_vars for each voucher type.

Also adds strip_color_codes() helper and comprehensive parametrized
tests covering all 32 voucher types.

Closes #154.
Improve error messages across 6 endpoint files by adding actionable
guidance to help bots self-heal from failed tool calls.

Changes:
- buy.lua: Add endpoint suggestions for empty shop/slot errors
- use.lua: Add card parameter guidance for consumable errors
- discard.lua/play.lua: Add card limit suggestions
- pack.lua: Add pack buying and target selection hints
- skip.lua: Add boss blind selection suggestion
- Update test_buy.py to match new error messages

Closes #148.
@S1M0N38 S1M0N38 changed the title BalatroBot v2 feat!: balatrobot v2 Feb 24, 2026
@S1M0N38 S1M0N38 marked this pull request as ready for review February 25, 2026 12:09
Copilot AI review requested due to automatic review settings February 25, 2026 12:09
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces version 2 of the balatrobot API with breaking changes (!). The main focus is on restructuring how tags are represented in the game state and improving error messages across all endpoints to be more actionable and helpful.

Changes:

  • Restructured tag representation from flat tag_name/tag_effect fields to nested tag objects with key, name, and effect fields
  • Added tags array to gamestate for tracking accumulated player-owned tags
  • Enhanced error messages across all endpoints with actionable guidance (e.g., suggesting reroll, sell, etc.)
  • Added support for selling jokers when Buffoon packs are open (SMODS_BOOSTER_OPENED state)
  • Implemented voucher effect extraction using game's localize function
  • Added comprehensive Tag enum definitions and test coverage

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/lua/utils/types.lua Updated Blind type to use nested Tag object instead of flat tag_name/tag_effect fields; added Tag class definition
src/lua/utils/openrpc.json Updated OpenRPC schema to reflect Tag object structure and enhanced sell endpoint description
src/lua/utils/gamestate.lua Implemented voucher effect extraction, tag ownership tracking, and updated blind tag structure
src/lua/utils/enums.lua Added comprehensive Tag.Key enum definitions for all Balatro tag types
src/lua/endpoints/sell.lua Added support for SMODS_BOOSTER_OPENED state with Buffoon pack validation
src/lua/endpoints/skip.lua Enhanced error message with actionable guidance
src/lua/endpoints/buy.lua Enhanced error messages with actionable guidance
src/lua/endpoints/add.lua Updated to support pack additions and refactored voucher handling to use dedicated SMODS function
src/lua/endpoints/use.lua Enhanced error messages with actionable guidance
src/lua/endpoints/play.lua Enhanced error message with actionable guidance
src/lua/endpoints/discard.lua Enhanced error messages with actionable guidance
src/lua/endpoints/pack.lua Enhanced error messages with actionable guidance
tests/lua/endpoints/test_skip.py Added tests for tag accumulation after skipping blinds
tests/lua/endpoints/test_pack.py Added tests for selling jokers during Buffoon pack selection
tests/lua/endpoints/test_gamestate.py Added comprehensive test coverage for voucher effects and tag structure
tests/lua/endpoints/test_buy.py Updated error message expectations
tests/lua/endpoints/test_add.py Updated error message expectations
docs/api.md Updated documentation to reflect new Tag structure and enhanced endpoint descriptions
Comments suppressed due to low confidence (4)

src/lua/endpoints/add.lua:409

  • The comment says "For jokers and consumables" but this else branch will also execute for vouchers and packs, creating unnecessary params that won't be used. Consider adding an explicit check: elseif card_type == "joker" or card_type == "consumable" then to match the comment and avoid creating unused params for vouchers and packs.
    else
      -- For jokers and consumables - just pass the key
      params = {
        key = args.key,
        skip_materialize = true,
        stickers = {},
        force_stickers = true,
      }

      -- Add edition if provided
      if edition_value then
        params.edition = edition_value
      end

      -- Add eternal if provided (jokers only - validation already done)
      if args.eternal then
        params.stickers[#params.stickers + 1] = "eternal"
      end

      -- Add perishable if provided (jokers only - validation already done)
      if args.perishable then
        params.stickers[#params.stickers + 1] = "perishable"
      end

      -- Add rental if provided (jokers only - validation already done)
      if args.rental then
        params.stickers[#params.stickers + 1] = "rental"
      end
    end

tests/lua/endpoints/test_skip.py:43

  • Grammar issue in comment: "because it used immediately" should be "because it is used immediately"
        assert "tag_investment" not in gamestate["tags"]  # because it used immediately

tests/lua/endpoints/test_skip.py:53

  • Grammar issue in comment: "because it used immediately" should be "because it is used immediately"
        assert "tag_investment" not in gamestate["tags"]  # because it used immediately

src/lua/utils/types.lua:58

  • Typo: "bilnd" should be "blind"
---@field status Blind.Status Status of the bilnd

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +52 to +53
assert gamestate["tags"][0]["key"] == "tag_polychrome"
assert "tag_investment" not in gamestate["tags"] # because it used immediately
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test file has a bug that will cause test_skip_big_boss to fail. The test at line 54-58 (not shown in diff) expects the error message "Cannot skip Boss blind" but skip.lua line 39 now returns "Cannot skip Boss blind. Use select to select and play the boss blind." The expected error message in the test needs to be updated to match the new implementation.

Copilot uses AI. Check for mistakes.
assert gamestate["blinds"]["big"]["status"] == "SKIPPED"
assert gamestate["blinds"]["boss"]["status"] == "SELECT"
assert gamestate["tags"][0]["key"] == "tag_polychrome"
assert "tag_investment" not in gamestate["tags"] # because it used immediately
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is checking if the string "tag_investment" is in a list of tag objects. Since gamestate["tags"] is a list of objects (each with "key", "name", "effect" fields), the in operator will never find a string match. This should likely be checking if any tag in the list has key == "tag_investment", such as: assert not any(tag["key"] == "tag_investment" for tag in gamestate["tags"])

Copilot uses AI. Check for mistakes.
assert gamestate["state"] == "BLIND_SELECT"
assert gamestate["blinds"]["boss"]["status"] == "SELECT"
assert gamestate["tags"][0]["key"] == "tag_polychrome"
assert "tag_investment" not in gamestate["tags"] # because it used immediately
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is checking if the string "tag_investment" is in a list of tag objects. Since gamestate["tags"] is a list of objects (each with "key", "name", "effect" fields), the in operator will never find a string match. This should likely be checking if any tag in the list has key == "tag_investment", such as: assert not any(tag["key"] == "tag_investment" for tag in gamestate["tags"])

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants