Dyota's blog

PowerShell: deck of cards

I started modelling a deck of normal Western, French-suit playing cards as a way to learn the basics of object-oriented programming.

I did it in PowerShell, just to see how far I can push it.

There are no games that you can actually play with this code - you can only set up a new, shuffled deck of 52 cards, deal cards from the deck, draw cards into your hand, and deal cards from your hand.

Things I learned in this exercise:

Here it is:

using namespace System.Collections.Generic; # for List
using namespace System.Management.Automation.Host; # for ChoiceDescription

Function Get-EnumValues{
    # get-enumValues -enum "System.Diagnostics.Eventing.Reader.StandardEventLevel"
    Param([string]$enum)
    $enumValues = @{}
    [enum]::getvalues([type]$enum) |
        ForEach-Object { 
            $enumValues.add($_, $_.value__)
        }
    $enumValues
}

enum Suit {
    spades = 0
    hearts = 1
    clubs = 2
    diamonds = 3
}

enum CardValue {
    ace = 1
    two = 2
    three = 3
    four = 4
    five = 5
    six = 6
    seven = 7
    eight = 8
    nine = 9
    ten = 10
    jack = 11
    queen = 12
    king = 13
}

class Card {
    [Suit] $suit
    [CardValue] $value
    [string] $symbol

    hidden [string[]] $symbols = @("♠","♥","♣","♦")
    hidden [string[]] $indeces = @('0','A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K')
    
    Card([Suit] $suit, [CardValue] $value) {
        $this.suit = $suit
        $this.value = $value
        $this.symbol = "$($this.indeces[$value])$($this.symbols[$suit])"
    }

    [string] Elaborate() {
        return "$($this.value) of $($this.suit)"
    }
}


# a hand and a deck are all "stacks"
class Stack {
    [List[Card]] $cardList

    Stack() {
        [List[Card]] $this.cardList = @()
    }

    [int] Count() { return $this.cardList.Count }

    [Card] Deal($i) {
        $dealtCard = $this.cardList[$i]
        $this.cardList.RemoveAt($i)
        return $dealtCard
    }

    [void] Add([Card]$card) {
        $this.cardList.Add($card)
    }

}

class Deck : Stack {

    Deck() {
        [Suit].GetEnumNames().ForEach({
            $_suit = $_
            [CardValue].GetEnumNames().ForEach({
                $_value = $_
                
                $this.Add(
                    [Card]::new($_suit, $_value)
                )
            })
        })
    }


    [Card] Deal() {
        $deckCount = $this.Count()

        $randomNumber = if ($deckCount -gt 1) {
            Get-Random ($deckCount - 1)
        } else {
            0
        }
        
        $dealCard = $this.Deal($randomNumber)

        # Write-Host Deal $dealCard.Elaborate() "," $this.Count() cards remaining

        return $dealCard
    }

}

class Hand : Stack {
    [List[Card]] Draw(
        [Deck] $pile,
        [int] $numberOfCards
    ){
        [List[Card]] $drawnCards = @()
        
        for ($i = 0; $i -lt $numberOfCards; $i++) {
            $drawnCard = $pile.Deal()
            [void] $this.Add($drawnCard)
            [void] $drawnCards.Add($drawnCard)
        }
        
        return $drawnCards
    }

    [Card] Discard() {
        
        [ChoiceDescription[]] $options = @()
        
        $options = $this.cardList | 
            %{
                return [ChoiceDescription]::new($_.symbol, $_.symbol)
            }

        
        [int] $discardIndex = (Get-Host).ui.PromptForChoice("Pick a card", "Choose a card to play/discard", $options, 0)
        
        $discardedCard = $this.cardList[$discardIndex]

        [void] $this.cardList.RemoveAt($discardIndex)

        return $discardedCard

    }
}
$hand = [Hand]::new()
$deck = [Deck]::new()
$discard = [Stack]::new()

[void] $hand.Draw($deck, 5)
[void] $hand.Discard()
[void] $hand.Discard()

#powershell