# 04d89ce8229216d73214c86390f7a0e838a04c2692a4cfdddde177179a6aa104 Elan 2.0.0-alpha5 valid
main
variable points set to 1000
variable rounds set to 1
variable game set to new Game(points)
call game.addPlayer(new AutomatedPlayerA(points))
call game.addPlayer(new AutomatedPlayerB(points))
while rounds > 0
call playOneRound(game)
call clearKeyBuffer()
call display(game)
assign rounds to rounds - 1
end while
end main
function strategyA(p as Player, dealerFaceUp as Card) returns Choice
return Choice.stand
end function
function strategyB(p as Player, dealerFaceUp as Card) returns Choice
return Choice.draw
end function
procedure playOneRound(game as Game)
call game.newRound()
call display(game)
variable dealer set to game.dealer
for player in game.players
call player.startTurn()
call display(game)
while player.status is Status.active
call player.nextAction(dealer.faceCard)
call display(game)
end while
end for
call dealer.play()
call display(game)
while dealer.status is Status.active
call dealer.nextAction(dealer.faceCard)
call display(game)
end while
call game.updatePoints()
end procedure
procedure display(game as Game)
variable html set to $"<style>{styleSheet}</style>{htmlForGame(game)}"
call displayHtml(html)
call sleep(1.5)
end procedure
function determinePlayerOutcome(dealer as Dealer, player as Player) returns Outcome
# These 'let' definitions are just to make the logic that follows them simpler to read
variable d set to dealer.status
variable dTotal set to dealer.handTotal
variable p set to player.status
variable pTotal set to player.handTotal
variable bust set to Status.bust
variable bj set to Status.blackjack
variable win set to Outcome.win
variable winDouble set to Outcome.winDouble
variable lose set to Outcome.lose
variable draw set to Outcome.draw
variable playerOutcome set to draw
if p is bust then
assign playerOutcome to lose
elif (p is bj) and (d isnt bj) then
assign playerOutcome to winDouble
elif d is bust then
assign playerOutcome to win
elif (d is bj) and (p is bj) then
assign playerOutcome to draw
elif p is bj then
assign playerOutcome to winDouble
elif d is bj then
assign playerOutcome to lose
elif pTotal > dTotal then
assign playerOutcome to win
elif pTotal < dTotal then
assign playerOutcome to lose
else
# strictly, this 'else' clause is redundant - as the variable was initialised to 'draw' - but added for clarity
assign playerOutcome to draw
end if
return playerOutcome
end function
function dealCard(random as Float) returns Card
variable number set to (random*52).floor()
variable rank set to rankValue().keys()[divAsInt(number, 4)]
variable suit set to number mod 4
return new Card(rank, intAsSuit(suit), false)
end function
function intAsSuit(n as Int) returns Suit
variable suit set to Suit.clubs
if n is 1 then
assign suit to Suit.diamonds
elif n is 2 then
assign suit to Suit.hearts
elif n is 3 then
assign suit to Suit.spades
end if
return suit
end function
function htmlForGame(game as Game) returns String
variable html set to "<div class='game'>"
assign html to html + htmlForPlayer(game.dealer)
for player in game.players
assign html to html + htmlForPlayer(player)
end for
assign html to html + $"<div class='message'>{game.message}</div>"
return html + "</div>"
end function
function htmlForPlayer(player as Player) returns String
variable html set to "<div class='player'>"
assign html to html + $"<div class='details'>{player.name} - {player.points} points {player.getMessage()}</div>"
assign html to html + "<div class='hand'>"
for card in player.cards
variable suit set to card.suit
variable rank set to card.rank
assign html to html + htmlForCard(card)
end for
return html + "</div></div>"
end function
function htmlForCard(card as Card) returns String
variable html set to ""
if card.faceDown then
assign html to "<div class='card reversed'>"
else
variable rank set to card.rank
variable suit set to card.suit
variable colour set to colourForSuit(suit)
variable symbol set to symbolForSuit(suit)
assign html to $"<div class='card {colour}'>"
variable u set to htmlForSpot("u", rank)
variable v set to htmlForSpot("v", symbol)
variable grid set to ""
for location in gridForRank(rank)
if location.equals("royal") then
assign grid to grid + htmlForSpot(location, rank)
else
assign grid to grid + htmlForSpot(location, symbol)
end if
end for
assign html to html + $"{u}{v}{grid}"
end if
return html + "</div>"
end function
function htmlForSpot(id as String, content as String) returns String
return $"<div class='{id}'>{content}</div>"
end function
class Game
constructor(dealerStartPoints as Int)
assign this.dealer to new Dealer(dealerStartPoints)
assign this.players to new List<of Player>()
assign this.message to ""
end constructor
property dealer as Dealer
property players as List<of Player>
property message as String
function withPlayers(p as List<of Player>) returns Game
let copyOfThis be copy(this)
assign copyOfThis.players to p
return copyOfThis
end function
procedure newRound()
variable dealer set to this.dealer
call dealer.newHand()
for player in this.players
call player.newHand()
end for
end procedure
procedure updatePoints()
for player in this.players
call player.determineOutcomeAndUpdatePoints(this.dealer)
end for
end procedure
procedure addPlayer(player as Player)
variable players set to this.players
call players.append(player)
end procedure
procedure setMessage(message as String)
assign this.message to message
end procedure
function toString() returns String
return "a Game"
end function
end class
class Card
property suit as Suit
property rank as String
property faceDown as Boolean
constructor(rank as String, suit as Suit, facedown as Boolean)
assign this.rank to rank
assign this.suit to suit
assign this.faceDown to facedown
end constructor
procedure turnFaceUp()
assign this.faceDown to false
end procedure
procedure turnFaceDown()
assign this.faceDown to true
end procedure
function toString() returns String
return $"{this.rank}{symbolForSuit(this.suit)}"
end function
end class
abstract class Player
property name as String
property points as Int
property cards as List<of Card>
property handTotal as Int
property softAce as Boolean
property status as Status
property hasTurn as Boolean
procedure startTurn()
if this.status is Status.active then
assign this.hasTurn to true
end if
end procedure
procedure determineOutcomeAndUpdatePoints(dealer as Dealer)
variable playerOutcome set to determinePlayerOutcome(dealer, this)
if playerOutcome is Outcome.winDouble then
call this.changePointsBy(2)
call dealer.changePointsBy(-2)
elif playerOutcome is Outcome.win then
call this.changePointsBy(1)
call dealer.changePointsBy(-1)
elif playerOutcome is Outcome.lose then
call this.changePointsBy(-1)
call dealer.changePointsBy(1)
end if
end procedure
procedure evaluateStatus(newCard as Card)
if (this.cardCount() is 2) and (this.handTotal is 21) then
assign this.status to Status.blackjack
elif (this.handTotal > 21) and (this.softAce) then
assign this.handTotal to this.handTotal - 10
assign this.softAce to false
elif this.handTotal > 21 then
assign this.status to Status.bust
elif this.handTotal is 21 then
assign this.status to Status.standing
end if
if this.status isnt Status.active then
assign this.hasTurn to false
end if
end procedure
procedure stand()
assign this.status to Status.standing
assign this.hasTurn to false
end procedure
procedure draw()
variable newCard set to dealCard(random())
variable cards set to this.cards
call cards.append(newCard)
if newCard.rank.equals("A") then
call this.addAce()
else
assign this.handTotal to this.handTotal + rankValue()[newCard.rank]
end if
call this.evaluateStatus(newCard)
end procedure
procedure addAce()
if this.softAce then
assign this.handTotal to this.handTotal + 1
else
assign this.handTotal to this.handTotal + 11
assign this.softAce to true
end if
end procedure
function cardCount() returns Int
return this.cards.length()
end function
procedure changePointsBy(amount as Int)
assign this.points to this.points + amount
end procedure
abstract procedure newHand()
procedure newHandHelper()
assign this.hasTurn to false
assign this.softAce to false
assign this.cards to new List<of Card>()
assign this.handTotal to 0
assign this.status to Status.active
call this.draw()
call this.draw()
end procedure
abstract function getMessage() returns String
function statusAsString() returns String
variable msg set to ""
variable status set to this.status
if this.hasTurn then
assign msg to msg + " - PLAYING"
elif status is Status.standing then
assign msg to msg + " - STANDING"
elif status is Status.blackjack then
assign msg to msg + " - BLACKJACK"
elif status is Status.bust then
assign msg to msg + " - BUST"
end if
return msg
end function
abstract procedure nextAction(dealerFaceCard as Card)
end class
class Dealer inherits Player
constructor(startingPoints as Int)
assign this.name to "Dealer"
assign this.points to startingPoints
assign this.cards to new List<of Card>()
assign this.faceCard to new Card("2", Suit.clubs, true)
end constructor
property faceCard as Card
property hasPlayed as Boolean
function withStatus(status as Status) returns Dealer
let copyOfThis be copy(this)
assign copyOfThis.status to status
return copyOfThis
end function
function withHandTotal(ht as Int) returns Dealer
let copyOfThis be copy(this)
assign copyOfThis.handTotal to ht
return copyOfThis
end function
procedure play()
call this.startTurn()
variable hiddenCard set to this.cards[1]
call hiddenCard.turnFaceUp()
assign this.hasPlayed to true
end procedure
procedure newHand()
assign this.hasPlayed to false
call this.newHandHelper()
assign this.faceCard to this.cards[0]
variable hiddenCard set to this.cards[1]
call hiddenCard.turnFaceDown()
end procedure
procedure nextAction(faceCard as Card)
if this.handTotal < 17 then
call this.draw()
else
call this.stand()
end if
end procedure
function getMessage() returns String
variable msg set to ""
if this.hasPlayed then
assign msg to this.statusAsString() + $" - hand total: {this.handTotal}"
end if
return msg
end function
function toString() returns String
return "the Dealer"
end function
end class
class HumanPlayer inherits Player
constructor(name as String, startingPoints as Int)
assign this.name to name
assign this.points to startingPoints
assign this.cards to new List<of Card>()
end constructor
function withStatus(status as Status) returns HumanPlayer
let copyOfThis be copy(this)
assign copyOfThis.status to status
return copyOfThis
end function
function withHandTotal(ht as Int) returns HumanPlayer
let copyOfThis be copy(this)
assign copyOfThis.handTotal to ht
return copyOfThis
end function
function withCards(c as List<of Card>) returns HumanPlayer
let copyOfThis be copy(this)
assign copyOfThis.cards to c
return copyOfThis
end function
procedure newHand()
call this.newHandHelper()
end procedure
procedure nextAction(dealerFaceCard as Card)
variable key set to ""
call clearKeyBuffer()
while key.equals("")
assign key to waitForKey()
if key.equals("d") then
call this.draw()
elif key.equals("s") then
call this.stand()
else
assign key to ""
end if
end while
end procedure
function getMessage() returns String
variable msg set to this.statusAsString() + $"- hand total: {this.handTotal}"
if this.hasTurn then
assign msg to msg + " - press 'd' to draw, 's' to stand"
end if
return msg
end function
function toString() returns String
return $"Player: {this.name}"
end function
end class
abstract class AutomatedPlayer inherits Player
procedure newHand()
call this.newHandHelper()
end procedure
procedure nextAction(dealerFaceCard as Card)
variable choice set to this.getChoice(dealerFaceCard)
if choice is Choice.draw then
call this.draw()
else
call this.stand()
end if
end procedure
abstract function getChoice(dealerFaceCard as Card) returns Choice
function getMessage() returns String
return this.statusAsString() + $" - hand total: {this.handTotal}"
end function
end class
class AutomatedPlayerA inherits AutomatedPlayer
constructor(startingPoints as Int)
assign this.name to "Player A"
assign this.points to startingPoints
assign this.cards to new List<of Card>()
end constructor
function getChoice(dealerFaceCard as Card) returns Choice
return strategyA(this, dealerFaceCard)
end function
function toString() returns String
return this.name
end function
end class
class AutomatedPlayerB inherits AutomatedPlayer
constructor(startingPoints as Int)
assign this.name to "Player B"
assign this.points to startingPoints
assign this.cards to new List<of Card>()
end constructor
function getChoice(dealerFaceCard as Card) returns Choice
return strategyB(this, dealerFaceCard)
end function
function toString() returns String
return this.name
end function
end class
enum Choice stand, draw
enum Outcome undecided, lose, draw, win, winDouble
enum Status active, standing, blackjack, bust
enum Suit clubs, diamonds, hearts, spades
function symbolForSuit(suit as Suit) returns String
variable dc set to [Suit.clubs:"♣", Suit.diamonds:"♦", Suit.hearts:"♥", Suit.spades:"♠"]
return dc[suit]
end function
function rankValue() returns Dictionary<of String, Int>
return ["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]
end function
function gridForRank(rank as String) returns List<of String>
variable dc set to ["A":["royal"], "2":["b", "c"], "3":["a", "b", "c"], "4":["d", "e", "f", "g"], "5":["a", "d", "e", "f", "g"], "6":["d", "e", "f", "g", "h", "i"], "7":["d", "e", "f", "g", "h", "i", "l"], "8":["d", "e", "f", "g", "h", "i", "l", "m"], "9":["a", "d", "e", "f", "g", "n", "o", "p", "r"], "10":["d", "e", "f", "g", "n", "o", "p", "r", "s", "t"], "J":["royal"], "Q":["royal"], "K":["royal"]]
return dc[rank]
end function
function colourForSuit(suit as Suit) returns String
variable dc set to [Suit.clubs:"black", Suit.diamonds:"red", Suit.hearts:"red", Suit.spades:"black"]
return dc[suit]
end function
constant styleSheet set to ":root { background-color: darkgreen; padding-left: 5px;}.game { padding: 5px;}.message, .details { color: white; font-family: Arial, Helvetica, sans-serif;}.hand { margin-top: 5px; height: 150px; padding-bottom: 10px; } .card { position: relative; float: left; background-color: white; width: 95px; height:140px; margin-right:10px; padding: 5px; border-radius: 5px; font-family: Helvetica, sans-serif; }.royal,.a,.b,.c,.d,.e,.f,.g,.h,.i,.j,.k,.l,.m,.n,.o,.p,.q,.r,.s,.t,.u,.v,.w,.x,.y,.z {position: absolute; text-align:center;}/* Standard spots */ .a,.b,.c,.d,.e,.f,.g,.h,.i,.l,.m,.n,.o,.p,.r,.s,.t {font-size: 30px;} /* columns */ .d,.n,.h,.p,.f {left: 18px } .a,.b,.c,.l,.m,.s,.t {left: 43px;} .e,.o,.i,.r,.g {left: 68px} /* rows */ .d,.b,.e {top: 0px} .s {top: 20px;} .l {top: 28px;} .n,.o {top: 37px;} .h,.a,.i {top: 57px} .p,.r {top: 75px;} .m {top: 86px;} .t {top: 93px;} .f,.c,.g {top: 114px;}/* royals */ .royal { position: absolute; z-index: 1; width: 95px; height: 140px; line-height: 140px; font-size: 100px; }/* corner summary */ .u {font-size: 15px; width: 15px; text-align: center; left: 0px; top: 2px;} .v {font-size: 20px; width: 15px; text-align: center; left: 0px; top: 12px;}/* suit colors */ .red {color: red} .black {color: black}/* back */ .card.reversed { background-color: rgba(0, 0, 255, 0.607);}"
Immediately file > auto save it to local file storage,
so that changes you make are preserved.
Move onto the next step before running the program.
Notes
Total hints used: /
Step 2: exploring the code
Briefly describe the meaning of 'abstraction' in programming:
Notes
Total hints used: /
Step 3: automated players
Having run the program 3-4 times, which of the two players is operating the very 'high risk' strategy, and which the very 'cautious' strategy?
Notes
Total hints used: /
Step 4: identifying the better of the strategies
Having run the program to play 10 rounds automatically, how many points did the Dealer, and each of the players ended up with?
Run the program a second time, and record how many points ended up with this time
Notes
Total hints used: /
Step 5: speeding up the program
Make sure that you have ghosted the sleep_ms (instruction 31) and all five occurrences of call display(game) within
the playOneRound procedure. Then run the program. You will see that both of the two players have lost points -
only the Dealer is ahead.
Which of the two players has lost the least points?
Given that the result is from playing 100 rounds, and each participant started with 1000 points, calculate the average points lost or gained per round expressed as a percentage,
for the Dealer and each Player (where a loss is a negative percentage and a gain is positive). Show how you worked this out.
Notes
Total hints used: /
Step 6: the default strategies
What can you say that every function does?
Notes
Total hints used: /
Step 7:implement a simple strategy with a decision
With the changes, how many points does each have after running 100 games?
Notes
Total hints used: /
Step 8: simulating 10,000 rounds
After making the changes such that the display now only updated after the last round, approximately how long did it take to run 10,000 rounds of Blackjack?
How many points did the Dealer, and each Player end up with?
Given that Player B is mimicking the strategy of the Dealer,
why are they still losing points to the Dealer? (If you can't see why, say so.)
Notes
Total hints used: /
Step 9: making use of the soft ace
After making the changes and running the new version, what were the points for Player A and Player B?
Notes
Total hints used: /
Step 10: paying attention to the Dealer's 'face up card'
After making the changes and running the new version, what were the points for Player A and Player B?
Notes
Total hints used: /
Step 11: expanding the scope of the previous rule
After making the changes and running the new version, what were the points for Player A and Player B?
Notes
Total hints used: /
Step 12: testing what you have learned
What does defining a named value as a 'variable' allow you to then do?
When defining a variable, what two things must you specify?
What kind of instruction is used to change the value of a variable?
What must the code written into the 'condition' field of an 'if' instruction evaluate to?
Some types offer 'properties'. What is a property?
If you have a named value and you want to see what properties (if any) you can
access from it, how do you do that?
What happens if you try to compare two values that are of different types?
If an 'if instruction' is given an 'else' clause, under what condition is that else clause executed?
If you combine more than one operator within a condition, what must you be careful about?
And what is the best way to ensure that you don't run into a problem when combining operators?
Notes
Total hints used: /
Congratulations! You have finished this worksheet
Answers to the questions in the previous step:
Q: What does defining a named value as a 'variable' allow you to then do?
A: It allows you to change the value subsequently.
When defining a variable, what two things must you specify?
You must specify the name for the variable and its initial value.
What kind of instruction is used to change the value of a variable?
A: a set instruction.
Q: What must the code written into the 'condition' field of an 'if' instruction evaluate to?
A: It must evaluate to 'true' or 'false'
Q: Some types offer 'properties'. What is a property?
A: a piece of information
Q: If you have a named value and you want to see what properties (if any) you can
access from it, how do you do that?
A: by typing a dot after the named value and selecting an option from the pop-up list
Q: What happens if you try to compare two values that are of different types?
A: You get a 'type incompatibility' error (like comparing apples and oranges)
Q: If you combine more than one operator within a condition, what must you be careful about?
A: operator precedence
Q: And what is the best way to ensure that you don't run into a problem when combining operators?
A: by using brackets to ensure that there is no ambiguity between which argument is applied to which operator.
If you are interested in taking this project further, watch this video first: