impl some hands
parent
ec81bb24ad
commit
0c406e3163
|
|
@ -1,5 +1,9 @@
|
|||
package entity
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Hand struct {
|
||||
Public []Card
|
||||
Private []Card
|
||||
|
|
@ -15,3 +19,38 @@ func (hand *Hand) Push(card Card) {
|
|||
func (hand Hand) Len() int {
|
||||
return len(hand.Public) + len(hand.Private)
|
||||
}
|
||||
|
||||
func (hand Hand) AllCards() []Card {
|
||||
allcards := make([]Card, 0, len(hand.Public)+len(hand.Private))
|
||||
for _, cards := range [][]Card{hand.Public, hand.Private} {
|
||||
allcards = append(allcards, cards...)
|
||||
}
|
||||
return allcards
|
||||
}
|
||||
|
||||
func (hand Hand) Flush() bool {
|
||||
suit := -1
|
||||
for _, card := range hand.AllCards() {
|
||||
if suit == -1 {
|
||||
suit = card.Suit
|
||||
} else if suit != card.Suit {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return suit != -1
|
||||
}
|
||||
|
||||
func (hand Hand) Straight() bool {
|
||||
cards := hand.AllCards()
|
||||
values := make([]int, 0, len(cards))
|
||||
for _, card := range cards {
|
||||
values = append(values, card.Value)
|
||||
}
|
||||
sort.Ints(values)
|
||||
for i := 1; i < len(values); i++ {
|
||||
if values[i-1]+1 != values[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return len(values) > 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
package entity
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHandAllCards(t *testing.T) {
|
||||
t.Run("all pub", func(t *testing.T) {
|
||||
hand := Hand{
|
||||
Public: []Card{
|
||||
Card{Value: 1},
|
||||
Card{Value: 2},
|
||||
},
|
||||
}
|
||||
all := hand.AllCards()
|
||||
if len(all) != 2 {
|
||||
t.Fatal(all)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("all pri", func(t *testing.T) {
|
||||
hand := Hand{
|
||||
Private: []Card{
|
||||
Card{Value: 1},
|
||||
Card{Value: 2},
|
||||
},
|
||||
}
|
||||
all := hand.AllCards()
|
||||
if len(all) != 2 {
|
||||
t.Fatal(all)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("both", func(t *testing.T) {
|
||||
hand := Hand{
|
||||
Public: []Card{
|
||||
Card{Value: 3},
|
||||
Card{Value: 4},
|
||||
},
|
||||
Private: []Card{
|
||||
Card{Value: 1},
|
||||
Card{Value: 2},
|
||||
},
|
||||
}
|
||||
all := hand.AllCards()
|
||||
if len(all) != 4 {
|
||||
t.Fatal(all)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHandStraight(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
public []Card
|
||||
private []Card
|
||||
want bool
|
||||
}{
|
||||
"empty": {
|
||||
want: false,
|
||||
},
|
||||
"one public card": {
|
||||
public: []Card{Card{Value: 3}},
|
||||
want: false,
|
||||
},
|
||||
"one private card": {
|
||||
private: []Card{Card{Value: 3}},
|
||||
want: false,
|
||||
},
|
||||
"two public card nonseq": {
|
||||
public: []Card{Card{Value: 3}, Card{Value: 1}},
|
||||
want: false,
|
||||
},
|
||||
"two private card nonseq": {
|
||||
private: []Card{Card{Value: 3}, Card{Value: 1}},
|
||||
want: false,
|
||||
},
|
||||
"one public card one private card nonseq": {
|
||||
public: []Card{Card{Value: 3}},
|
||||
private: []Card{Card{Value: 1}},
|
||||
want: false,
|
||||
},
|
||||
"two public card seq": {
|
||||
public: []Card{Card{Value: 3}, Card{Value: 2}},
|
||||
want: true,
|
||||
},
|
||||
"two private card seq": {
|
||||
private: []Card{Card{Value: 3}, Card{Value: 2}},
|
||||
want: true,
|
||||
},
|
||||
"one public card one private card seq": {
|
||||
public: []Card{Card{Value: 2}},
|
||||
private: []Card{Card{Value: 1}},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, d := range cases {
|
||||
c := d
|
||||
t.Run(name, func(t *testing.T) {
|
||||
hand := Hand{Public: c.public, Private: c.private}
|
||||
got := hand.Straight()
|
||||
if got != c.want {
|
||||
t.Fatal(c.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
package operation
|
||||
|
||||
import (
|
||||
"local/sandbox/cards/src/entity"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func royalFlush(game *entity.Game, _ interface{}) int {
|
||||
hand := getHand(game)
|
||||
if !hand.Flush() || !hand.Straight() {
|
||||
return 0
|
||||
}
|
||||
for _, card := range hand.AllCards() {
|
||||
if card.Value < 11 {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func straightFlush(game *entity.Game, _ interface{}) int {
|
||||
hand := getHand(game)
|
||||
if !hand.Flush() || !hand.Straight() {
|
||||
return 0
|
||||
}
|
||||
biggest := 0
|
||||
for _, card := range hand.AllCards() {
|
||||
if card.Value > biggest {
|
||||
biggest = card.Value
|
||||
}
|
||||
}
|
||||
return biggest
|
||||
}
|
||||
|
||||
func fourOfAKind(game *entity.Game, _ interface{}) int {
|
||||
hand := getHand(game)
|
||||
counts := counts(hand)
|
||||
best := entity.Card{}
|
||||
high := entity.Card{}
|
||||
for k, v := range counts {
|
||||
if v >= 4 && k.Value > best.Value {
|
||||
best = k
|
||||
}
|
||||
if v != 4 && k.Value > high.Value {
|
||||
high = k
|
||||
}
|
||||
}
|
||||
if best.Value == 0 {
|
||||
return 0
|
||||
}
|
||||
return best.Value*100 + high.Value
|
||||
}
|
||||
|
||||
func fullHouse(game *entity.Game, _ interface{}) int {
|
||||
hand := getHand(game)
|
||||
counts := counts(hand)
|
||||
trio := entity.Card{}
|
||||
duo := entity.Card{}
|
||||
for k, v := range counts {
|
||||
if v == 3 && k.Value > trio.Value {
|
||||
trio = k
|
||||
}
|
||||
if v == 2 && k.Value > duo.Value {
|
||||
duo = k
|
||||
}
|
||||
}
|
||||
if trio.Value == 0 || duo.Value == 0 {
|
||||
return 0
|
||||
}
|
||||
return trio.Value*100 + duo.Value
|
||||
}
|
||||
|
||||
func flush(game *entity.Game, _ interface{}) int {
|
||||
hand := getHand(game)
|
||||
if !hand.Flush() {
|
||||
return 0
|
||||
}
|
||||
values := make([]int, 0)
|
||||
for _, card := range hand.AllCards() {
|
||||
values = append(values, card.Value)
|
||||
}
|
||||
sort.Ints(values)
|
||||
rank := 0
|
||||
for i := len(values) - 1; i >= 0; i-- {
|
||||
rank = rank*100 + values[i]
|
||||
}
|
||||
return rank
|
||||
}
|
||||
|
||||
func straight(game *entity.Game, _ interface{}) int {
|
||||
hand := getHand(game)
|
||||
if !hand.Straight() {
|
||||
return 0
|
||||
}
|
||||
big := 0
|
||||
for _, card := range hand.AllCards() {
|
||||
if card.Value > big {
|
||||
big = card.Value
|
||||
}
|
||||
}
|
||||
return big
|
||||
}
|
||||
|
||||
func threeOfAKind(game *entity.Game, _ interface{}) int {
|
||||
panic("not impl")
|
||||
}
|
||||
|
||||
func twoPair(game *entity.Game, _ interface{}) int {
|
||||
panic("not impl")
|
||||
}
|
||||
|
||||
func pair(game *entity.Game, _ interface{}) int {
|
||||
panic("not impl")
|
||||
}
|
||||
|
||||
func highCard(game *entity.Game, _ interface{}) int {
|
||||
panic("not impl")
|
||||
}
|
||||
|
||||
func getHand(game *entity.Game) entity.Hand {
|
||||
player := game.Players[game.Current.Turn]
|
||||
return player.Hand
|
||||
}
|
||||
|
||||
func counts(hand entity.Hand) map[entity.Card]int {
|
||||
cards := hand.AllCards()
|
||||
m := make(map[entity.Card]int, len(cards))
|
||||
for _, card := range cards {
|
||||
if _, ok := m[card]; !ok {
|
||||
m[card] = 0
|
||||
}
|
||||
m[card] += 1
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
package operation
|
||||
|
||||
import (
|
||||
"local/sandbox/cards/src/entity"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testHandCase struct {
|
||||
public []entity.Card
|
||||
private []entity.Card
|
||||
want int
|
||||
}
|
||||
|
||||
func TestStraight(t *testing.T) {
|
||||
cases := map[string]testHandCase{
|
||||
"no cards": {
|
||||
want: 0,
|
||||
},
|
||||
"straight big 3": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 11},
|
||||
entity.Card{Value: 12},
|
||||
entity.Card{Value: 13},
|
||||
},
|
||||
want: 13,
|
||||
},
|
||||
"straight sm 3": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 1},
|
||||
entity.Card{Value: 2},
|
||||
entity.Card{Value: 3},
|
||||
},
|
||||
want: 3,
|
||||
},
|
||||
"straight sm 2": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 1},
|
||||
entity.Card{Value: 2},
|
||||
},
|
||||
want: 2,
|
||||
},
|
||||
"not a straight": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 1},
|
||||
entity.Card{Value: 3},
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for name, d := range cases {
|
||||
testHand(t, name, straight, d.public, d.private, d.want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlush(t *testing.T) {
|
||||
cases := map[string]testHandCase{
|
||||
"no cards": {
|
||||
want: 0,
|
||||
},
|
||||
"not a flush": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 1, Suit: 0},
|
||||
entity.Card{Value: 2, Suit: 1},
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
"big, big, small, small flush": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 12},
|
||||
entity.Card{Value: 10},
|
||||
entity.Card{Value: 2},
|
||||
entity.Card{Value: 1},
|
||||
},
|
||||
want: 12100201,
|
||||
},
|
||||
"small, small flush": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 2},
|
||||
entity.Card{Value: 1},
|
||||
},
|
||||
want: 201,
|
||||
},
|
||||
"big, small flush": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 11},
|
||||
entity.Card{Value: 1},
|
||||
},
|
||||
want: 1101,
|
||||
},
|
||||
"big, big flush": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 11},
|
||||
entity.Card{Value: 12},
|
||||
},
|
||||
want: 1211,
|
||||
},
|
||||
}
|
||||
|
||||
for name, d := range cases {
|
||||
testHand(t, name, flush, d.public, d.private, d.want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFullHouse(t *testing.T) {
|
||||
cases := map[string]testHandCase{
|
||||
"no cards": {
|
||||
want: 0,
|
||||
},
|
||||
"10 > 11": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 11},
|
||||
entity.Card{Value: 11},
|
||||
entity.Card{Value: 10},
|
||||
entity.Card{Value: 10},
|
||||
entity.Card{Value: 10},
|
||||
},
|
||||
want: 1011,
|
||||
},
|
||||
"five of a kind": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
"two pair": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 4},
|
||||
entity.Card{Value: 4},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 6},
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for name, d := range cases {
|
||||
testHand(t, name, fullHouse, d.public, d.private, d.want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFourOfAKind(t *testing.T) {
|
||||
cases := map[string]testHandCase{
|
||||
"no cards": {
|
||||
want: 0,
|
||||
},
|
||||
"one card": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 5},
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
"two cards": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
"three cards": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
"four cards": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
},
|
||||
want: 500,
|
||||
},
|
||||
"five cards": {
|
||||
public: []entity.Card{
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
entity.Card{Value: 5},
|
||||
},
|
||||
want: 505,
|
||||
},
|
||||
}
|
||||
|
||||
for name, d := range cases {
|
||||
testHand(t, name, fourOfAKind, d.public, d.private, d.want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStraightFlush(t *testing.T) {
|
||||
cases := map[string]testHandCase{
|
||||
"no cards": {
|
||||
want: 0,
|
||||
},
|
||||
"one nonroyal cards": {
|
||||
public: []entity.Card{entity.Card{Value: 5}},
|
||||
want: 0,
|
||||
},
|
||||
"one royal cards": {
|
||||
public: []entity.Card{entity.Card{Value: 12}},
|
||||
want: 0,
|
||||
},
|
||||
"two nonroyal cards": {
|
||||
public: []entity.Card{entity.Card{Value: 5}, entity.Card{Value: 6}},
|
||||
want: 6,
|
||||
},
|
||||
"twe royal cards": {
|
||||
public: []entity.Card{entity.Card{Value: 12}, entity.Card{Value: 13}},
|
||||
want: 13,
|
||||
},
|
||||
"twe mix cards": {
|
||||
public: []entity.Card{entity.Card{Value: 11}, entity.Card{Value: 10}},
|
||||
want: 11,
|
||||
},
|
||||
}
|
||||
|
||||
for name, d := range cases {
|
||||
testHand(t, name, straightFlush, d.public, d.private, d.want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoyalFlush(t *testing.T) {
|
||||
cases := map[string]testHandCase{
|
||||
"no cards": {
|
||||
want: 0,
|
||||
},
|
||||
"one nonroyal cards": {
|
||||
public: []entity.Card{entity.Card{Value: 5}},
|
||||
want: 0,
|
||||
},
|
||||
"one royal cards": {
|
||||
public: []entity.Card{entity.Card{Value: 12}},
|
||||
want: 0,
|
||||
},
|
||||
"two nonroyal cards": {
|
||||
public: []entity.Card{entity.Card{Value: 5}, entity.Card{Value: 6}},
|
||||
want: 0,
|
||||
},
|
||||
"twe royal cards": {
|
||||
public: []entity.Card{entity.Card{Value: 12}, entity.Card{Value: 13}},
|
||||
want: 1,
|
||||
},
|
||||
"twe mix cards": {
|
||||
public: []entity.Card{entity.Card{Value: 11}, entity.Card{Value: 10}},
|
||||
want: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for name, d := range cases {
|
||||
testHand(t, name, royalFlush, d.public, d.private, d.want)
|
||||
}
|
||||
}
|
||||
|
||||
func testHand(t *testing.T, name string, foo Int, public []entity.Card, private []entity.Card, want int) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
hand := entity.Hand{
|
||||
Public: public,
|
||||
Private: private,
|
||||
}
|
||||
game := &entity.Game{
|
||||
Players: []entity.Player{
|
||||
entity.Player{Hand: hand},
|
||||
},
|
||||
}
|
||||
got := foo(game, nil)
|
||||
if got != want {
|
||||
t.Fatal(want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,5 +1,41 @@
|
|||
package operation
|
||||
|
||||
import "local/sandbox/cards/src/entity"
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"local/sandbox/cards/src/entity"
|
||||
)
|
||||
|
||||
type Int func(*entity.Game, interface{}) int
|
||||
|
||||
var intStringified = map[string]Int{
|
||||
"royalFlush": royalFlush,
|
||||
"straightFlush": straightFlush,
|
||||
"fourOfAKind": fourOfAKind,
|
||||
"fullHouse": fullHouse,
|
||||
"flush": flush,
|
||||
"straight": straight,
|
||||
"threeOfAKind": threeOfAKind,
|
||||
"twoPair": twoPair,
|
||||
"pair": pair,
|
||||
"highCard": highCard,
|
||||
}
|
||||
|
||||
func (foo *Int) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return foo.FromString(s)
|
||||
}
|
||||
|
||||
func (foo *Int) FromString(s string) error {
|
||||
for k, v := range intStringified {
|
||||
if k == s {
|
||||
*foo = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("unknown int method " + s)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue