# 980341479e819248ffb83f0d1b4475d9516772c2501bc7e359a6d419beddffd0 Elan 1.6.1 guest default_profile valid

[imported] function createDealer(startPoints as Int) returns Dealer
  return new Dealer(startPoints)
end function

[imported] procedure updateScores(dealer as Dealer, player as Player, outcome as Outcome)
  if outcome is Outcome.winDouble then
    call player.changeScoreBy(2)
    call dealer.changeScoreBy(-2)
  else if outcome is Outcome.win then
    call player.changeScoreBy(1)
    call dealer.changeScoreBy(-1)
  else if outcome is Outcome.lose then
    call player.changeScoreBy(-1)
    call dealer.changeScoreBy(1)
  end if
end procedure

[imported] function determineOutcome(dealer as Dealer, player as Player) returns Outcome
  let d be dealer.hand.status
  let dTot be dealer.hand.total
  let p be player.hand.status
  let pTot be player.hand.total
  let bust be Status.bust
  let bj be Status.blackjack
  let win be Outcome.win
  let winDouble be Outcome.winDouble
  let lose be Outcome.lose
  let draw be Outcome.draw
  return if p is bust then lose else if d is bust then win else if (d is bj) and (p is bj) then draw else if p is bj then winDouble else if d is bj then lose else if pTot > dTot then win else if pTot < dTot then lose else draw
end function

[imported] function dealCard(random as Float) returns Card
  let number be (random*52).floor()
  let rank be rankValue.keys()[number div 4]
  let suit be number mod 4
  return new Card() with rank set to rank, suit set to suit
end function

[imported] interface Player
  abstract property hand as Hand

  abstract procedure changeScoreBy(amount as Int)

end interface

[imported] abstract class Automated inherits Player
  property hand as Hand

  property score as Int

  property dealerFaceCard as Card

  property active as Boolean

  abstract function getAction() returns Action

  procedure nextMove()
    if getAction() is Action.draw then
      call property.hand.draw()
    else
      call property.hand.stand()
    end if
  end procedure

  procedure playHandOut()
    call start()
    while property.hand.status is Status.playing
      call nextMove()
    end while
  end procedure

  procedure start()
    set property.active to true
  end procedure

  procedure changeScoreBy(amount as Int)
    set property.score to property.score + amount
  end procedure

end class

[imported] class Dealer inherits Automated
  constructor(startingPoints as Int)
    set property.score to startingPoints
  end constructor

  procedure newHand(faceCard as Card)
    set property.dealerFaceCard to faceCard
    set property.active to false
    set property.hand to new Hand()
    call property.hand.addCard(faceCard)
    call property.hand.draw()
  end procedure

  function getAction() returns Action
    return if property.hand.total < 17 then Action.draw else Action.stand
  end function

  function asHtml() returns String
    return if property.active then "Dealer: {property.hand.asHtml()}" else "Dealer: {property.dealerFaceCard.asHtml()} {unicode(9633)}"
  end function

end class

[imported] class Hand
  property softAce as Boolean

  property cards as List<of Card>

  property count as Int

  property total as Int

  property status as Status

  function isPairOf8s() returns Boolean
    return (property.count is 2) and (property.cards[0].value() is 8) and (property.cards[1].value() is 8)
  end function

  procedure setStatus(status as Status)
    set property.status to status
  end procedure

  procedure stand()
    set property.status to Status.standing
  end procedure

  procedure draw()
    set property.status to Status.playing
    let card be dealCard(random())
    call addCard(card)
  end procedure

  procedure addCard(card as Card)
    call property.cards.append(card)
    set property.count to property.count + 1
    if card.isAce() then
      call addAce()
    else
      set property.total to property.total + card.value()
    end if
    if property.total > 21 then
      call useUpSoftAceIfPossible()
    else if card.isAce() then
      set property.softAce to true
    end if
    if (property.count is 2) and (property.total is 21) and property.softAce then
      set property.status to Status.blackjack
    else if property.total > 21 then
      set property.status to Status.bust
    else if property.total is 21 then
      set property.status to Status.standing
    end if
  end procedure

  procedure addAce()
    if property.softAce then
      set property.total to property.total + 1
    else
      set property.total to property.total + 11
      set property.softAce to true
    end if
  end procedure

  private procedure useUpSoftAceIfPossible()
    if property.softAce then
      set property.total to property.total - 10
      set property.softAce to false
    else
      set property.status to Status.bust
    end if
  end procedure

  function asHtml() returns String
    variable str set to ""
    each card in property.cards
      set str to str + card.asHtml() + " "
    end each
    set str to "{str}({property.total}) {property.status}"
    return str
  end function

end class

[imported] record Card
  property suit as Int

  property rank as String

  function value() returns Int
    return rankValue[property.rank]
  end function

  function isAce() returns Boolean
    return property.rank is "A"
  end function

  function asHtml() returns String
    let blackSuit be (property.suit is 0) or (property.suit is 3)
    let colour be if blackSuit then "black" else "red"
    let tag be htmlEscChars[property.suit]
    return "{property.rank}<span style='color: {colour}'>{tag}</span>"
  end function

end record

[imported] enum Action stand, draw

[imported] enum Outcome undecided, lose, draw, win, winDouble

[imported] enum Status pending, playing, standing, blackjack, bust

[imported] constant rankValue set to {"2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "10":10, "J":10, "Q":10, "K":10, "A":11}

[imported] constant htmlEscChars set to {"&clubs;", "&diams;", "&hearts;", "&spades;"}
