Added MiniScript implementation of 71_Poker.

This commit is contained in:
JoeStrout
2023-09-30 17:02:12 -07:00
parent bd1815d5da
commit 75b0ce9c46
4 changed files with 588 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
print ""*30 + "POETRY"
print ""*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
print " "*30 + "POETRY"
print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
print; print; print
I = 0; J = 0; K = 0; U = 0

View File

@@ -0,0 +1,18 @@
Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html).
Conversion to [MiniScript](https://miniscript.org).
Ways to play:
1. Command-Line MiniScript:
Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as:
```
miniscript poker.ms
```
2. Mini Micro:
Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter:
```
load "poker"
run
```

View File

@@ -0,0 +1,557 @@
print " "*33 + "POKER"
print " "*15 + "Creative Computing Morristown, New Jersey"
print; print; print
print "Welcome to the casino. We each have $200."
print "I will open the betting before the draw; you open after."
print "To fold bet 0; to check bet .5."
print "Enough talk -- let's get down to business."
print
askYesNo = function(prompt)
while true
yn = input(prompt + "? ").lower + " "
if yn[0] == "y" then return "yes"
if yn[0] == "n" then return "no"
print "Answer yes or no, please."
end while
end function
askNumber = function(prompt, maxQty=3, minQty=1, maxErr=null, minErr=null)
while true
value = input(prompt + "? ").val
if minQty <= value <= maxQty then return value
if value < minQty and minErr != null then
print minErr
else if maxErr != null then
print maxErr
else
print "Enter a value between " + minQty + " and " + maxQty + ", please."
end if
end while
end function
random = function(n)
return floor(n * rnd)
end function
rand10 = function
return floor(10 * rnd)
end function
pad = function(s, width)
return s + " " * (width - s.len)
end function
// Bonus little feature when running in Mini Micro: display a header bar
// always at the top of the screen, showing current balances. Does nothing
// on other platforms.
drawHeaders = function
if version.hostName != "Mini Micro" then return
display(2).mode = displayMode.text; td = display(2)
td.color = color.black; td.backColor = text.color
td.row = 25; td.column = 0
td.print pad("Computer: $" + computer.balance, 25) +
pad("Table: $" + Table.pot, 25) +
pad("Player: $" + human.balance, 18)
end function
// Card class: represents a single playing card
Card = {}
Card.rank = 0 // from 2 to 14 (Ace)
Card.suit = "Clubs"
Card.keep = false // temp flag, used to note which cards to keep vs. discard
Card.make = function(rank, suit)
result = new Card
result.rank = rank
result.suit = suit
return result
end function
Card.rankStr = function(plural=false)
if self.rank > 10 then
return ["Jack", "Queen", "King", "Ace"][self.rank-11] + "s"*plural
else
return str(self.rank) + "'s" * plural
end if
end function
Card.str = function
return self.rankStr + " of " + self.suit
end function
// Prepare a standard deck of 52 cards, and functions to draw and discard
deck = []
for suit in ["Clubs", "Diamonds", "Hearts", "Spades"]
for rank in range(2, 14)
deck.push Card.make(rank, suit)
end for
end for
deck.shuffle
discardPile = []
drawCard = function
if not deck then
globals.deck = discardPile
deck.shuffle
globals.discardPile = []
end if
return deck.pop
end function
discard = function(cardOrCards)
if cardOrCards isa Hand then
globals.discardPile += cardOrCards.cards
else if cardOrCards isa list then
globals.discardPile += cardOrCards
else
discardPile.push cardOrCards
end if
end function
// Hand ranks: how we compare Poker hands
HandRank = {}
HandRank.value = 0
HandRank.str = function(highCard); return ""; end function
HandRank.make = function(value)
result = new HandRank
result.value = value
return result
end function
HandRank.None = new HandRank
HandRank.Schmaltz = HandRank.make(1)
HandRank.Schmaltz.str = function(c); return "schmaltz, " + c.rankStr + " high"; end function
HandRank.PartialStraight = HandRank.make(2)
HandRank.PartialStraight.str = function(c); return ""; end function // (no display string; this is used only internally)
HandRank.Pair = HandRank.make(3)
HandRank.Pair.str = function(c); return "a pair of " + c.rankStr(true); end function
HandRank.TwoPair = HandRank.make(4)
HandRank.TwoPair.str = function(c); return "two pair, " + c.rankStr(true); end function
HandRank.Three = HandRank.make(5)
HandRank.Three.str = function(c); return "three " + c.rankStr(true); end function
HandRank.Straight = HandRank.make(6)
HandRank.Straight.str = function(c); return "straight, " + c.rankStr + " high"; end function
HandRank.Flush = HandRank.make(7)
HandRank.Flush.str = function(c); return "a flush in " + c.suit; end function
HandRank.FullHouse = HandRank.make(8)
HandRank.FullHouse.str = function(c); return "full house, " + c.rankStr(true); end function
HandRank.Four = HandRank.make(9)
HandRank.Four.str = function(c); return "four " + c.rankStr(true); end function
// Note: original code does not detect a straight flush or royal flush.
// Hand: represents a set of cards in the hand of one player.
Hand = {}
Hand.cards = null // list of Card
Hand.rank = null // HandRank instance
Hand.highCard = null // reference to which (of self.cards) determines relative value
Hand.afterDraw = false // true if we've already had a chance to draw cards
Hand.make = function(cards)
result = new Hand
result.cards = cards
result.analyze
return result
end function
Hand.deal = function
return Hand.make([drawCard, drawCard, drawCard, drawCard, drawCard])
end function
Hand.replaceCard = function(index)
discard self.cards[index]
self.cards[index] = drawCard
end function
Hand.rankStr = function
return self.rank.str(self.highCard)
end function
Hand.isWeak = function
return self.rank.value < HandRank.PartialStraight.value or
(self.rank.value == HandRank.PartialStraight.value and self.afterDraw) or
(self.rank <= HandRank.TwoPair.value and self.highCard.rank <= 6)
end function
Hand.beats = function(other)
if self.rank.value > other.rank.value then return true
if self.rank.value < other.rank.value then return false
return self.highCard.rank > other.highCard.rank
end function
Hand.print = function(startingNumber=1)
num = startingNumber
for c in self.cards
s = " " * (num < 10) + num + " -- " + c.str
print " " + s, ""
if num % 2 == 0 then
print
else
print " " * (28-s.len), ""
end if
num += 1
end for
if num % 2 == 0 then print
end function
Hand.analyze = function
allSameSuit = true
for i in range(1, self.cards.len-1)
if self.cards[i].suit != self.cards[0].suit then allSameSuit = false
end for
if allSameSuit then
self.rank = HandRank.Flush
self.highCard = self.cards[0]
return
end if
sortedCards = self.cards[:]
sortedCards.sort "rank"
self.rank = HandRank.Schmaltz
for c in sortedCards; c.keep = false; end for
keepAny = false
for i in range(0, sortedCards.len-2)
matchesNextCard = (sortedCards[i].rank == sortedCards[i+1].rank)
if matchesNextCard then
self.highCard = sortedCards[i]
matchesPrevCard = (i > 0 and sortedCards[i].rank == sortedCards[i-1].rank)
sortedCards[i].keep = true
sortedCards[i+1].keep = true
keepAny = true
if self.rank.value < HandRank.Pair.value then
self.rank = HandRank.Pair
else if matchesPrevCard and self.rank == HandRank.Pair then
self.rank = HandRank.Three
else if self.rank == HandRank.Pair then
self.rank = HandRank.TwoPair
else if self.rank == HandRank.TwoPair then
self.rank = HandRank.FullHouse
else if matchesPrevCard then
self.rank = HandRank.Four
else
self.rank = HandRank.FullHouse
end if
end if
end for
if not keepAny then
if sortedCards[3].rank - sortedCards[0].rank == 3 then
for i in range(0,3); sortedCards[i].keep = true; end for
self.rank = HandRank.PartialStraight
end if
if sortedCards[4].rank - sortedCards[1].rank == 3 then
if self.rank == HandRank.PartialStraight then
self.rank = HandRank.Straight
sortedCards[4].keep = true
self.highCard = sortedCards[4]
else
self.rank = HandRank.PartialStraight
for i in range(1,4); sortedCards[i].keep = true; end for
end if
end if
end if
if self.rank == HandRank.Schmaltz then
self.highCard = sortedCards[4]
sortedCards[4].keep = true
sortedCards[3].keep = true
end if
end function
// Some global constants, just to make the code more understandable
Ante = 5
// Player -- base class for computer and human
Player = {}
Player.balance = 200
Player.hand = null
Player.totalBet = 0
Player.anteUp = function
self.balance -= Ante
return Ante
end function
Player.newHand = function
if self.hand then discard self.hand
self.hand = Hand.deal
self.totalBet = 0
end function
Player.addToPot = function(amount)
self.balance -= amount
Table.pot += amount
drawHeaders
end function
Player.win = function
self.balance += Table.pot
Table.pot = 0
drawHeaders
end function
// strategies the computer player might employ
Strategy = {}
Strategy.make = function(name, value=2, drawCount=null)
result = new Strategy
result.name = name
result.value = value
result.drawCount = drawCount
return result
end function
Strategy.fold = Strategy.make("FOLD")
Strategy.check = Strategy.make("CHECK")
Strategy.raise = Strategy.make("RAISE", 2)
Strategy.bluff = function(value, drawCount); return Strategy.make("BLUFF", value, drawCount); end function
Strategy.bet = function(value); return Strategy.make("BET", value); end function
// computer player
computer = new Player
computer.strategy = null
computer.newHand = function
super.newHand
if self.hand.isWeak then
if rand10 < 2 then
self.strategy = Strategy.bluff(23, 2)
else if rand10 < 2 then
self.strategy = Strategy.bluff(23, 1)
else if rand10 < 1 then
self.strategy = Strategy.bluff(23, 0)
else
self.strategy = Strategy.fold
end if
else if self.hand.rank.value < HandRank.Three.value then
if rand10 < 2 then self.strategy = Strategy.bluff(23, null) else self.strategy = Strategy.check
else if self.hand.rank.value < HandRank.FullHouse.value then
self.strategy = Strategy.bet(35)
else
if rand10 < 1 then self.strategy = Strategy.bet(35) else self.strategy = Strategy.raise
end if
end function
computer.bet = function(minBet=1, openingBet=false)
//print "My hand: "; self.hand.print; print "Strategy: " + self.strategy
if self.balance < minBet then
print "I fold."
return 0
end if
if openingBet and (self.strategy == Strategy.check or self.strategy == Strategy.fold) then
print "I check."
return 0.5
else if self.strategy == Strategy.fold and human.totalBet > 5 then
print "I fold."
return 0
else if self.strategy == Strategy.check then
print "I'll see you."
return minBet
else if openingBet then
result = self.strategy.value + rand10
if result > self.balance then result = self.balance
if result == 0 then
print "I check."
return 0.5
end if
print "I'll open with $" + result
return result
else
bet = self.strategy.value + rand10
if self.strategy == Strategy.raise then bet += minBet
if bet > self.balance then bet = self.balance
raise = bet - minBet
if raise <= 0 then
print "I'll see you."
return minBet
else
print "I'll see you, and raise you " + raise
return bet
end if
end if
end function
computer.drawCards = function
//print "My hand:"; self.hand.print
drawCount = 0
for c in self.hand.cards; if not c.keep then drawCount += 1; end for
print "I am taking " + drawCount + " card" + "s" * (drawCount != 1)
for i in self.hand.cards.indexes
if not self.hand.cards[i].keep then self.hand.replaceCard i
end for
self.hand.analyze
//print "My new hand: "; self.hand.print
if self.strategy.name == "BLUFF" then
self.strategy = Strategy.bluff(28)
else if self.hand.isWeak then
self.strategy = Strategy.fold
else if self.hand.rank.value < HandRank.Three.value then
if rand10 == 0 then self.strategy = Strategy.bet(19) else self.strategy = Strategy.raise
else if self.hand.rank.value < HandRank.FullHouse.value then
if rand10 == 0 then self.strategy = Strategy.bet(11) else self.strategy = Strategy.bet(19)
else
self.strategy = Strategy.raise
end if
end function
computer.win = function
print; print "I win."
super.win
end function
computer.checkFunds = function
if self.balance >= Ante then return
if human.balance < 50 then return // BUGFIX
if human.hasWatch or askYesNo("Would you like to buy back your watch for $50") == "no" then
print "I'm busted. Conglatulations!"
exit
end if
self.balance += 50
// Note: original BASIC code does not take money from the player, but let's fix that:
human.balance -= 50 // BUGFIX
human.hasWatch = true
drawHeaders
end function
human = new Player
human.hasWatch = true
human.bet = function(minBet=1, openingBet=false)
while true
betStr = input("What is your bet? ")
bet = betStr.val
if bet == 0 and betStr != "0" then
print "Enter 0 to fold, 0.5 to check, or a value of 1 or more to bet."
continue
else if bet == 0.5 and openingBet then
return bet // (check)
else if 0 < bet < 1 then
print "No small change, please."
continue
else if bet < minBet then
print "If you can't see my bet, then fold."
continue
else if bet > self.balance then
print "You can't bet with what you haven't got."
self.trySellWatch
continue
end if
return bet
end while
end function
human.drawCards = function
qty = askNumber("How many cards do you want", 3, 0, "You can't draw more than three cards.")
if qty == 0 then return
print "What are their numbers: "
for i in range(1, qty)
num = askNumber("", 5, 1)
self.hand.replaceCard num - 1
end for
print "Your new hand:"
self.hand.print
self.hand.analyze
end function
human.win = function
print; print "You win."
super.win
end function
human.checkFunds = function
if self.balance < Ante then self.trySellWatch
if self.balance < Ante then
print "Your wad is shot. So long, sucker!"
exit
end if
end function
human.trySellWatch = function
if not self.hasWatch then return
if rand10 < 7 then
value = 75
msg = "I'll give you $" + value + " for it."
else
value = 25
msg = "That's a pretty crummy watch - I'll give you $" + value + "."
end if
if computer.balance < value then return // BUGFIX
if askYesNo("Would you like to sell your watch") == "no" then return
print msg
self.balance += value
self.hasWatch = false
// Note: the original BASIC program does not actually take any money from the computer.
// But let's do it right here:
computer.balance -= value // BUGFIX
drawHeaders
end function
Table = {}
Table.pot = 0
deal = function
Table.pot += computer.anteUp + human.anteUp
drawHeaders
computer.newHand
human.newHand
end function
// Do a round of betting. Return true to continue, or false
// if either player folds (ending the hand).
takeBets = function(computerFirst)
if computerFirst then
bet = computer.bet(1, true)
if bet == 0 then
human.win
return false
end if
if bet == 0.5 then bet = 0 // "check" (no bet, but stay in the hand)
computer.addToPot bet
else
bet = 0
end if
raise = bet
canCheck = (bet == 0)
while true
bet = human.bet(raise, canCheck)
if bet == 0 then
computer.win
return false
end if
if bet == 0.5 then // (checked)
if computerFirst then return true
bet = 0
else
human.addToPot bet
raise = bet - raise
if raise == 0 then return true
end if
if computerFirst or bet > 0 then canCheck = false
bet = computer.bet(raise, canCheck)
if bet == 0 then
human.win
return false
end if
if bet == 0.5 then return true // (checked)
canCheck = false
computer.addToPot bet
raise = bet - raise
if raise == 0 then return true
end while
end function
playHand = function
print
computer.checkFunds
human.checkFunds
print "The ante is " + Ante + ". I will deal:"
print
deal
print "Your hand:"
human.hand.print
print
if not takeBets(true) then return
print; print "Now we draw -- ", ""
human.drawCards
computer.drawCards
if not takeBets(false) then return
print; print "Now we compare hands:"
print "My hand:"
computer.hand.print 6
print
print "You have " + human.hand.rankStr
print "I have " + computer.hand.rankStr
if computer.hand.beats(human.hand) then
computer.win
else if human.hand.beats(computer.hand) then
human.win
else
print "The hand is drawn."
print "All $" + Table.pot + " remains in the pot."
end if
end function
drawHeaders
while true
playHand
print "Now I have $" + computer.balance + " and you have $" + human.balance
if askYesNo("Do you wish to continue") == "no" then break
end while

View File

@@ -15,6 +15,17 @@ As published in Basic Computer Games (1978):
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
#### Known Bugs
- If you bet more than the computer has, it will still see you, resulting in a negative balance. (To handle this properly, the computer would need to go "all in" and reduce your bet to an amount it can match; or else lose the game, which is what happens to the human player in the same situation.)
- If you are low on cash and sell your watch, then make a bet much smaller than the amount you just gained from the watch, it sometimes nonetheless tells you you "blew your wad" and ends the game.
- When the watch is sold (in either direction), the buyer does not actually lose any money.
- The code in the program about selling your tie tack is unreachable due to a logic bug.
#### Porting Notes
(please note any difficulties or challenges in porting here)