Skip to content

Commit

Permalink
Merge pull request #21 from eleniums/ai-updates
Browse files Browse the repository at this point in the history
Machine AI update
  • Loading branch information
eleniums authored Apr 16, 2020
2 parents 3a9171e + 3f7038b commit 232df80
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 64 deletions.
15 changes: 4 additions & 11 deletions ai/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,11 @@ func NewMachine(modelFile string) *Machine {

// Action returns the action the player wants to make with his hand from the given array of possible actions.
func (ai *Machine) Action(dealer *game.Hand, player *game.Hand, actions []game.Action) game.Action {
prediction := ai.model.Predict(dealer, player)
action, result := prediction.Split()
p := ai.model.PredictAll(dealer, player)

if allowed(actions, action) {
// if surrender is recommended, take the surrender
if action == game.ActionSurrender {
return action
}

// if result is positive, proceed with action
if result == game.ResultWin || result == game.ResultTie || result == game.ResultNone {
return action
for _, v := range p {
if allowed(actions, v.Action) && v.Result != game.ResultLoss {
return v.Action
}
}

Expand Down
84 changes: 84 additions & 0 deletions machine/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package machine

import (
"sort"

"github.com/dmitryikh/leaves"
"github.com/eleniums/blackjack/game"
)

// Model can make predictions from a model.
type Model struct {
model *leaves.Ensemble
}

// NewModel will load the specified model file.
func NewModel(modelFile string) *Model {
model, err := leaves.XGEnsembleFromFile(modelFile, false)
if err != nil {
panic(err)
}

return &Model{
model: model,
}
}

// Predict will feed a dealer hand and player hand into a model and return the resulting prediction with the highest score.
func (m *Model) Predict(dealer *game.Hand, player *game.Hand) Prediction {
predictions := m.predictInternal(dealer, player)

// get the index of the highest value in the array
index := 0
maxValue := 0.0
for i, v := range predictions {
if v > maxValue {
index = i
maxValue = v
}
}

action, result := Label(index).Split()

return Prediction{
Action: action,
Result: result,
Score: maxValue,
}
}

// PredictAll will feed a dealer hand and player hand into a model and return all resulting predictions with their scores.
func (m *Model) PredictAll(dealer *game.Hand, player *game.Hand) []Prediction {
predictions := m.predictInternal(dealer, player)

results := []Prediction{}
for i, v := range predictions {
action, result := Label(i).Split()
results = append(results, Prediction{
Action: action,
Result: result,
Score: v,
})
}

sort.Slice(results, func(i, j int) bool {
return results[i].Score > results[j].Score
})

return results
}

// predictInternal will feed a dealer hand and player hand into a model and return the resulting predictions.
func (m *Model) predictInternal(dealer *game.Hand, player *game.Hand) []float64 {
d := float64(ConvertHand(FormatHand(dealer)))
p := float64(ConvertHand(FormatHand(player)))

predictions := make([]float64, m.model.NOutputGroups())
fvals := []float64{d, p}
err := m.model.Predict(fvals, 0, predictions)
if err != nil {
panic(err)
}

return predictions
}
53 changes: 0 additions & 53 deletions machine/predict.go

This file was deleted.

12 changes: 12 additions & 0 deletions machine/prediction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package machine

import (
"github.com/eleniums/blackjack/game"
)

// Prediction contains a single outcome from a model.
type Prediction struct {
Action game.Action
Result game.Result
Score float64
}

0 comments on commit 232df80

Please sign in to comment.