Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support new MaskedDeck operations: StatsBase.sample!, Base.popat! and "restore!" #35

Merged
merged 1 commit into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/CodeCov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Set up Julia
uses: julia-actions/setup-julia@latest
with:
version: 1.7.2
version: 1.9

- name: Test with coverage
env:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ jobs:
fail-fast: false
matrix:
version:
- '1.3'
- '1.4'
- '1.5'
- '1.6'
- '1.7'
- '1.8'
- '1.9'
os:
- ubuntu-latest
- macOS-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@latest
with:
version: 1.7.2
version: 1.9
- name: Install dependencies
run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
- name: Build and deploy
Expand Down
5 changes: 3 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
name = "PlayingCards"
uuid = "ecfe714a-bcc2-4d11-ad00-25525ff8f984"
authors = ["Charles Kawczynski <kawczynski.charles@gmail.com>"]
version = "0.3.2"
version = "0.4.0"

[deps]
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"

[compat]
julia = "1.3"
julia = "1.5"
12 changes: 6 additions & 6 deletions bors.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
status = [
"ci 1.3 - ubuntu-latest",
"ci 1.3 - windows-latest",
"ci 1.3 - macOS-latest",
"ci 1.4 - ubuntu-latest",
"ci 1.4 - windows-latest",
"ci 1.4 - macOS-latest",
"ci 1.5 - ubuntu-latest",
"ci 1.5 - windows-latest",
"ci 1.5 - macOS-latest",
Expand All @@ -14,6 +8,12 @@ status = [
"ci 1.7 - ubuntu-latest",
"ci 1.7 - windows-latest",
"ci 1.7 - macOS-latest",
"ci 1.8 - ubuntu-latest",
"ci 1.8 - windows-latest",
"ci 1.8 - macOS-latest",
"ci 1.9 - ubuntu-latest",
"ci 1.9 - windows-latest",
"ci 1.9 - macOS-latest",
"docbuild",
]
delete_merged_branches = true
Expand Down
55 changes: 49 additions & 6 deletions src/masked_deck.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import StatsBase
const SB = StatsBase

"""
MaskedDeck()

Expand All @@ -17,12 +20,13 @@
"""
struct MaskedDeck{T <: Vector} <: AbstractDeck
cards::T
len::Vector{Int}
mask::BitVector # cards in deck
end
Base.length(deck::MaskedDeck) = deck.len[1]
Base.length(deck::MaskedDeck) = count(deck.mask)
function MaskedDeck()
cards = PlayingCards.full_deck()
return MaskedDeck{typeof(cards)}(cards, Int[length(cards)])
mask = BitVector(ntuple(_->true, 52))
return MaskedDeck{typeof(cards)}(cards, mask)
end

Base.pop!(deck::MaskedDeck, n::Integer) = Base.pop!(deck, Val(n))
Expand All @@ -32,12 +36,51 @@
end

function Base.pop!(deck::MaskedDeck)::Card
@inbounds deck.len[1] -= 1
return @inbounds deck.cards[deck.len[1]+1]
i = findlast(1:52) do i
@inbounds deck.mask[i]
end
@inbounds deck.mask[i] = false
return @inbounds deck.cards[i]
end

function Base.popat!(deck::MaskedDeck, card::Card)::Card
i = findfirst(c->c==card, deck.cards)
@assert !isnothing(i)
deck.mask[i] = false
return deck.cards[i]
end

function restore!(deck::MaskedDeck, card::Card)
i = findfirst(c->c==card, deck.cards)
@assert !isnothing(i)
@inbounds deck.mask[i] = true
return nothing
end

SB.sample!(deck::MaskedDeck)::Card =
SB.sample!(Random.default_rng(), deck)

function SB.sample!(rng, deck::MaskedDeck)::Card
@assert any(deck.mask)
@inbounds begin
while true
s = SB.sample(rng, 1:52)
if deck.mask[s]
deck.mask[s] = false
return deck.cards[s]
end
end

Check warning on line 72 in src/masked_deck.jl

View check run for this annotation

Codecov / codecov/patch

src/masked_deck.jl#L72

Added line #L72 was not covered by tests
end
end

function Base.copyto!(x::MaskedDeck, y::MaskedDeck)
x.cards .= y.cards
x.mask .= y.mask
x
end

function reset!(deck::MaskedDeck)
@inbounds deck.len[1] = length(deck.cards)
deck.mask .= true
return nothing
end

Expand Down
1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[deps]
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
26 changes: 25 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Test
using StatsBase
const SB = StatsBase
using PlayingCards
const PC = PlayingCards
using PlayingCards: rank_string
using PlayingCards: MaskedDeck

Expand Down Expand Up @@ -102,6 +105,7 @@ end
cards = pop!(deck, Val(2))
@test length(cards)==2
@test length(deck)==50
@test count(deck.mask)==50
@test length(full_deck())==52

# Test pop! correctness against regular deck
Expand All @@ -122,9 +126,29 @@ end
# Allocations
pop!(mdeck, Val(2))
p_allocated = @allocated pop!(mdeck, Val(2))
@test p_allocated == 0
if VERSION ≥ v"1.7"
@test p_allocated == 0
end

shuffle!(mdeck)
p_allocated = @allocated shuffle!(mdeck)
@test p_allocated == 0

mdeck = MaskedDeck()
c = SB.sample!(mdeck)
@test count(mdeck.mask) == 51
end

@testset "More MaskedDeck" begin
mdeck = MaskedDeck()
c = Base.popat!(mdeck, A♡)
@test mdeck.mask[findfirst(c->c==A♡, mdeck.cards)] == false
PC.restore!(mdeck, A♡)
@test count(mdeck.mask) == 52

amdeck = MaskedDeck()
bmdeck = MaskedDeck()
Base.copyto!(amdeck, bmdeck)
p_allocated = @allocated Base.copyto!(amdeck, bmdeck)
@test p_allocated == 0
end
Loading