diff --git a/cmd/cli/main.go b/cmd/cli/main.go index b104a53..7da1ab6 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -9,6 +9,7 @@ import ( "git.tuxpa.in/a/card_id/common/game" "git.tuxpa.in/a/card_id/common/game/card" + "git.tuxpa.in/a/card_id/common/game/score" ) func main() { @@ -16,15 +17,27 @@ func main() { if err != nil { log.Panicln(err) } - g := game.Game{Dealer: deal, Lives: 3} trial := g.CreateTrial(3, 1) + g.CurrentTrial = 1 + fmt.Println("type c[n] to select a card, e.g. c1") + fmt.Println("exit to exit") for { if trial != nil { - if trial.CheckSelection() { - fmt.Print("\n match found!!!") + sc, lives := trial.CheckSelection() + if sc != 0 { + fmt.Println(" match found!!!") + g.Combo = g.Combo + 1 + g.Score = g.Score + sc*score.DefaultTable.Ratio(g.Combo)*g.CurrentTrial + } else { + g.Combo = 0 } + g.Lives = g.Lives + lives + msg := fmt.Sprintf(` +lives: %0.4v | score: %d | combo: %d +`, g.Lives, g.Score, g.Combo) + fmt.Printf(msg) } fmt.Printf("\ncardid > ") var args [4]string @@ -45,20 +58,15 @@ func main() { case "select", "sel", "c": if alen > 1 { num, _ := strconv.Atoi(args[1]) - cd, err := trial.SelectCard(num) + _, err := trial.SelectCard(num) if err != nil { log.Println(err) } - if cd.Id > 0 { - msg := fmt.Sprintf(` -card %d was %s | lives: %d -current hand: -%s`, num, cd.Name, trial.Lives, trial.ShowString()) - fmt.Printf(msg) - } + } else { trial.SelectCard(0) } + fmt.Printf("hand: %s\n", trial.ShowString()) case "debug": fmt.Println(trial) case "exit", "quit", "q": diff --git a/common/data/combo.go b/common/data/combo.go new file mode 100644 index 0000000..0ad59c2 --- /dev/null +++ b/common/data/combo.go @@ -0,0 +1 @@ +package data diff --git a/common/game/card/card.go b/common/game/card/card.go index 4d2fcbb..82a52e1 100644 --- a/common/game/card/card.go +++ b/common/game/card/card.go @@ -1,16 +1,47 @@ package card -import "git.tuxpa.in/a/card_id/common/data" +import ( + "strings" + + "git.tuxpa.in/a/card_id/common/data" + "lukechampine.com/frand" +) type Card struct { - Id int `json:"id"` - Name string `json:"name"` + Id int `json:"id"` + Name string `json:"name"` + CleanName string `json:"clean_name"` + Rate float64 `json:"rate"` + + BonusLife bool `json:"bonus_life"` } func (c *Card) FromResult(d data.Result) Card { c.Id = d.Id c.Name = d.Name c.Rate = d.Rate + c.CleanName = strings.ReplaceAll(strings.TrimSpace(stripNum(c.Name)), " ", " ") return *c } + +func (c *Card) CopyWithBonus(chance float64) Card { + return Card{ + Id: c.Id, + Rate: c.Rate, + Name: c.Name, + CleanName: c.CleanName, + BonusLife: (frand.Float64() <= chance), + } +} + +func stripNum(s string) string { + var result strings.Builder + for i := 0; i < len(s); i++ { + b := s[i] + if !('0' <= b && b <= '9') { + result.WriteByte(b) + } + } + return result.String() +} diff --git a/common/game/card/dealer.go b/common/game/card/dealer.go index 7295d33..a61d88d 100644 --- a/common/game/card/dealer.go +++ b/common/game/card/dealer.go @@ -21,6 +21,25 @@ func NewDealer() *Dealer { } } +func (g *Dealer) CardNames() []string { + out := map[string]struct{}{} + for _, br := range g.Brands { + for _, c := range br.RewardDeck.Cards { + out[c.CleanName] = struct{}{} + } + if br.SpecialDeck != nil { + for _, c := range br.SpecialDeck.Cards { + out[c.CleanName] = struct{}{} + } + } + } + keys := make([]string, 0, len(out)) + for k := range out { + keys = append(keys, k) + } + return keys +} + func (g *Dealer) ReadFromRoot(datadir string) (*Dealer, error) { if datadir == "" { datadir = "./data" diff --git a/common/game/card/deck.go b/common/game/card/deck.go index bf4dd35..aca4957 100644 --- a/common/game/card/deck.go +++ b/common/game/card/deck.go @@ -13,10 +13,13 @@ type Deck struct { Cards []Card TotalProbability float64 + + BonusLifeProbability float64 } func NewDeck(r data.Results) *Deck { out := &Deck{} + out.BonusLifeProbability = 0.005 out.Cards = make([]Card, 0, len(r)) for _, v := range r { out.Cards = append(out.Cards, new(Card).FromResult(v)) @@ -31,7 +34,7 @@ func (t *Deck) Draw() Card { for _, v := range t.Cards { sofar = sofar + v.Rate if sofar >= selector-0.000000001 { - return v + return v.CopyWithBonus(t.BonusLifeProbability) } } return Card{ diff --git a/common/game/game.go b/common/game/game.go index 7cfb910..84f350b 100644 --- a/common/game/game.go +++ b/common/game/game.go @@ -6,8 +6,31 @@ import ( ) type Game struct { - Lives int - Dealer *card.Dealer + Lives float64 + Score int + + Combo int + BestCombo int + CurrentTrial int + + Trial *Trial + Trials []Trial + + Player string + PlayerId string + + Dealer *card.Dealer `json:"-"` +} + +func (g *Game) New(user string, dealer *card.Dealer) *Game { + return &Game{ + Dealer: dealer, + Player: user, + } +} + +func (g *Game) Resolve(action string, args []string) { + } func (g *Game) CreateTrial(pairs int, extra int) *Trial { @@ -37,7 +60,7 @@ func (g *Game) CreateTrial(pairs int, extra int) *Trial { } frand.Shuffle(len(cards), func(i, j int) { cards[i], cards[j] = cards[j], cards[i] }) - trial := NewTrial(g.Lives) + trial := NewTrial() for k, v := range cards { trial.Cards[k+1] = v } diff --git a/common/game/score/score.go b/common/game/score/score.go new file mode 100644 index 0000000..9884395 --- /dev/null +++ b/common/game/score/score.go @@ -0,0 +1,71 @@ +package score + +type ComboTable interface { + Ratio(int) int +} + +type MapTable struct { + m map[int]int +} + +func (t *MapTable) Ratio(i int) int { + if v, ok := t.m[i]; ok { + return v + } + return 1 +} + +var DefaultTable ComboTable = &MapTable{ + m: map[int]int{ + 0: 1, + 1: 2, + 2: 4, + 3: 7, + 4: 12, + 5: 19, + 6: 28, + 7: 39, + 8: 52, + 9: 67, + 10: 84, + 11: 103, + 12: 124, + 13: 147, + 14: 172, + 15: 199, + 16: 228, + 17: 259, + 18: 292, + 19: 327, + 20: 364, + 21: 364, + 22: 364, + 23: 364, + 24: 364, + 25: 364, + 26: 364, + 27: 364, + 28: 364, + 29: 364, + 30: 364, + 31: 364, + 32: 364, + 33: 364, + 34: 364, + 35: 364, + 36: 364, + 37: 364, + 38: 364, + 39: 364, + 40: 364, + 41: 364, + 42: 364, + 43: 364, + 44: 364, + 45: 364, + 46: 364, + 47: 364, + 48: 364, + 49: 364, + 50: 364, + }} diff --git a/common/game/trial.go b/common/game/trial.go index a3613a2..31ff44f 100644 --- a/common/game/trial.go +++ b/common/game/trial.go @@ -12,7 +12,6 @@ import ( type Trial struct { Cards map[int]card.Card - Lives int SelectionState int PendingSelection [2]int @@ -21,9 +20,8 @@ type Trial struct { History [][2]int } -func NewTrial(lives int) *Trial { +func NewTrial() *Trial { return &Trial{ - Lives: lives, Cards: make(map[int]card.Card, 7), Shown: make(map[int]card.Card, 7), PendingSelection: [2]int{-1, -1}, @@ -34,9 +32,6 @@ func (t *Trial) SelectCard(id int) (card.Card, error) { if id > len(t.Cards) { return card.Card{}, errs.Invalid("index out of bounds") } - if t.Lives == 0 { - return card.Card{}, errs.Logic("game over") - } switch t.SelectionState { case 0: // start selection t.SelectionState = 1 @@ -51,36 +46,37 @@ func (t *Trial) SelectCard(id int) (card.Card, error) { } } -func (t *Trial) CheckSelection() bool { +func (t *Trial) CheckSelection() (points int, lifechange float64) { switch t.SelectionState { case 0, 1: - return false + return default: } - defer func() { t.SelectionState = 0 }() - + lifechange = -1 c1, ok := t.Cards[t.PendingSelection[0]] if !ok { - return false + return } c2, ok := t.Cards[t.PendingSelection[1]] if !ok { - return false + return } if c1.Id == c2.Id { if c1.Name == c2.Name { t.Shown[t.PendingSelection[0]] = c1 t.Shown[t.PendingSelection[1]] = c2 - return true + if c1.BonusLife || c2.BonusLife { + return 10, 1 + } + return 10, 0.35 } } t.History = append(t.History, t.PendingSelection) t.PendingSelection = [2]int{-1, -1} - t.Lives = t.Lives - 1 - return false + return 0, -1 } func (t *Trial) Show() map[int]card.Card { @@ -102,8 +98,8 @@ func (t *Trial) ShowString() string { } outs := "" - for _, k := range t.CardKeys() { - piece := "[???]" + for n, k := range t.CardKeys() { + piece := fmt.Sprintf("[??%d??]", n+1) if v, ok := out[k]; ok { piece = fmt.Sprintf("[%s]", v.Name) }