Splendor-Style Card Purchases with Gem Costs, Color Discounts and Concurrent Buys

Read the full interview experience this question came from →

Quick Overview

Model a Splendor-style card game in code: check whether a player's five-color gem inventory can afford a card, make an all-or-nothing purchase, and apply discounts earned from cards already owned. A follow-up asks how to keep one player's simultaneous purchase requests safe and consistent.

Splendor-Style Card Purchases with Gem Costs, Color Discounts and Concurrent Buys

Company: Brex

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Technical Screen

Build a small simulation of buying cards in a game modeled on the board game Splendor. Players hold gems in five colors: Blue (`B`), White (`W`), Green (`G`), Red (`R`) and Yellow (`Y`). Each card has a cost, given as a number of gems of each color, and a color of its own. The data model and the code structure are up to you. Implement the three tasks below in order, then answer the concurrency follow-up. Each part builds on the previous one. ### Constraints and Clarifications - Gem counts and card costs are non-negative integers. A color that a cost does not mention costs zero. - A player has a gem inventory and a hand of purchased cards. - The interview phrased the functions with `card` and `player_gems` arguments. Because a purchase also changes the player's hand, you may pass a player object that holds both the gems and the hand. ### Clarifying Questions - Are cards drawn from a market shared by several players, so that a bought card disappears for everyone, or is card availability out of scope? - Do spent gems go back to a shared bank, or do they simply leave the player's inventory? - Should invalid input, such as a negative cost or an unknown color, raise an error or return `False`? ### Part 1 — Check whether a player can afford a card Implement `can_purchase(card, player_gems)`, which returns `True` if the player's gems cover the card's cost and `False` otherwise. ```hint Compare color by color Pick a representation for costs and inventories that turns the check into one pass over the colors, with no special case for colors a card does not use. ``` #### What This Part Should Cover - A clear model for colors, gem counts and cards - A per-color comparison that treats an unmentioned color as zero - Tests for an exactly affordable card, a card short by one gem of one color, and a free card ### Part 2 — Buy a card Implement `purchase(card, player_gems)`. It first checks whether the player can afford the card. If so, it adds the card to the player's hand, deducts the cost from the player's gems, and returns `True`; otherwise it returns `False`. ```hint All or nothing A failed purchase must leave both the gems and the hand exactly as they were. Decide the order of the check and the updates so that this holds by construction. ``` #### What This Part Should Cover - Reuse of the affordability check rather than a second copy of it - No partial deduction when the purchase fails - Tests of the gems and the hand after a successful purchase and after a failed one ### Part 3 — Discounts from owned cards Add discounts. Each card already in the player's hand counts toward later purchases as one gem of that card's color: one card of a color pays for one gem of the same color. For example, a player whose hand holds 2 White cards and 1 Red card, and who wants a card costing 4 White and 1 Red, pays only 2 White gems. Update `can_purchase` and `purchase` accordingly. ```hint Derive or maintain The discount is a function of the hand. Decide whether to recompute it at every purchase or keep it up to date as cards are bought, and what each choice costs. ``` #### Clarifying Questions for this Part - When a color's discount exceeds the card's cost in that color, is the excess simply unused, or can it pay for other colors? #### What This Part Should Cover - The effective cost per color when the discount is smaller than, equal to, or larger than the cost - Affordability and payment both based on the discounted cost - The example from the prompt reproduced as a test ### Part 4 — Concurrent purchases by the same player The system now runs in a multi-threaded or parallel environment, and the same player may send several purchase requests at the same moment. How would you change the design to keep it safe under concurrency and keep the player's data consistent? ```hint Find the race Write down an interleaving of two purchases by one player that lets them spend the same gems twice. ``` ```hint Where the state lives The fix differs depending on whether the player's state lives in one process's memory or in a database shared by several servers. Consider both. ``` #### What This Part Should Cover - The check-then-act race on the gems, and on the hand that drives the discount - Making the check and every update of one purchase atomic per player - Lock granularity, and the deadlock risk once a purchase touches more than one shared object - Retries, and a test that exercises concurrent purchases ### What a Strong Answer Covers - A data model that extends from Part 1 to Part 3 without a rewrite - Small, readable functions with tests at every part, including the example from the prompt - Correct edge cases: free cards, colors missing from a cost or an inventory, and discounts larger than costs - A concurrency design that matches where the state lives, with its cost in contention and complexity ### Follow-up Questions - If cards come from a market shared by several players, how do you stop two players from buying the same card? - A client times out and resends a purchase request. How do you make sure the player is charged only once? - How would you support one request that buys several cards, all or none?

Overview: Model a Splendor-style card game in code: check whether a player's five-color gem inventory can afford a card, make an all-or-nothing purchase, and apply discounts earned from cards already owned. A follow-up asks how to keep one player's simultaneous purchase requests safe and consistent.

Read the full Brex Software Engineer interview experience this question came from

|Home/Software Engineering Fundamentals/Brex
Brex logo
Brex
Aug 21, 2026
mediumSoftware EngineerTechnical ScreenSoftware Engineering Fundamentals
1
0

Build a small simulation of buying cards in a game modeled on the board game Splendor. Players hold gems in five colors: Blue (B), White (W), Green (G), Red (R) and Yellow (Y). Each card has a cost, given as a number of gems of each color, and a color of its own. The data model and the code structure are up to you.

Implement the three tasks below in order, then answer the concurrency follow-up. Each part builds on the previous one.

Constraints and Clarifications

  • Gem counts and card costs are non-negative integers. A color that a cost does not mention costs zero.
  • A player has a gem inventory and a hand of purchased cards.
  • The interview phrased the functions with card and player_gems arguments. Because a purchase also changes the player's hand, you may pass a player object that holds both the gems and the hand.

Clarifying Questions Guidance

  • Are cards drawn from a market shared by several players, so that a bought card disappears for everyone, or is card availability out of scope?
  • Do spent gems go back to a shared bank, or do they simply leave the player's inventory?
  • Should invalid input, such as a negative cost or an unknown color, raise an error or return False ?

Part 1 — Check whether a player can afford a card

Implement can_purchase(card, player_gems), which returns True if the player's gems cover the card's cost and False otherwise.

What This Part Should Cover Guidance

  • A clear model for colors, gem counts and cards
  • A per-color comparison that treats an unmentioned color as zero
  • Tests for an exactly affordable card, a card short by one gem of one color, and a free card

Part 2 — Buy a card

Implement purchase(card, player_gems). It first checks whether the player can afford the card. If so, it adds the card to the player's hand, deducts the cost from the player's gems, and returns True; otherwise it returns False.

What This Part Should Cover Guidance

  • Reuse of the affordability check rather than a second copy of it
  • No partial deduction when the purchase fails
  • Tests of the gems and the hand after a successful purchase and after a failed one

Part 3 — Discounts from owned cards

Add discounts. Each card already in the player's hand counts toward later purchases as one gem of that card's color: one card of a color pays for one gem of the same color. For example, a player whose hand holds 2 White cards and 1 Red card, and who wants a card costing 4 White and 1 Red, pays only 2 White gems. Update can_purchase and purchase accordingly.

Clarifying Questions for this Part Guidance

  • When a color's discount exceeds the card's cost in that color, is the excess simply unused, or can it pay for other colors?

What This Part Should Cover Guidance

  • The effective cost per color when the discount is smaller than, equal to, or larger than the cost
  • Affordability and payment both based on the discounted cost
  • The example from the prompt reproduced as a test

Part 4 — Concurrent purchases by the same player

The system now runs in a multi-threaded or parallel environment, and the same player may send several purchase requests at the same moment. How would you change the design to keep it safe under concurrency and keep the player's data consistent?

What This Part Should Cover Guidance

  • The check-then-act race on the gems, and on the hand that drives the discount
  • Making the check and every update of one purchase atomic per player
  • Lock granularity, and the deadlock risk once a purchase touches more than one shared object
  • Retries, and a test that exercises concurrent purchases

What a Strong Answer Covers Guidance

  • A data model that extends from Part 1 to Part 3 without a rewrite
  • Small, readable functions with tests at every part, including the example from the prompt
  • Correct edge cases: free cards, colors missing from a cost or an inventory, and discounts larger than costs
  • A concurrency design that matches where the state lives, with its cost in contention and complexity

Follow-up Questions Guidance

  • If cards come from a market shared by several players, how do you stop two players from buying the same card?
  • A client times out and resends a purchase request. How do you make sure the player is charged only once?
  • How would you support one request that buys several cards, all or none?
Loading comments...