# Demonstrates: a bidding round that can stop midway (its own turn variable instead of a turn
# loop), payoffs larger than 1 (score), and a utility range for OpenSpiel.
game: auction
label: Auction
players: "2 players"
num_players: 2
mode: competitive
summary: >
  One item worth 4 is up for auction. Player 1 starts with a bid of 0. On your turn you either
  raise the bid by 1 (up to at most 4) or pass. Whoever passes leaves the item to the other player
  at the current bid. The winner gets the item's value minus the bid; the other player gets nothing.

constants:
  VALUE: 4
  MAX_BID: 4

# Payoffs lie between 0 and 4. OpenSpiel algorithms use this range.
utility: {min: 0, max: 4}

state:
  vars:
    bid: {scope: global, type: int, init: 0}
    current: {scope: global, type: ref, init: 0}
    winner: {scope: global, type: ref, init: null}
    done: {scope: global, type: bool, init: false}

flow:
  id: game
  label: Auction
  kind: loop
  # No over_x: a turn loop would still give the other player a turn in the same pass after
  # someone passed. Here each action hands over the turn itself, and the loop stops via until_x.
  until: someone has passed
  until_x: done
  children:
    - {id: bidding, label: Raise or pass, kind: step, actor: current player, actor_x: current, do: [raise, pass]}

actions:
  raise:
    pre: bid is below the maximum
    pre_x: "bid < MAX_BID"
    effect:
      - {op: add, var: bid, amount: 1}
      - {op: set, var: current, value_x: "1 - current"}
  pass:
    effect:
      - {op: set, var: winner, value_x: "1 - current"}
      - {op: set, var: done, value: true}

end:
  - id: sold
    if: "someone has passed"
    if_x: done
    result: score
    returns_x: "[(VALUE - bid) if p == winner else 0 for p in players]"
