Skip to content

Commit

Permalink
Implemented "leaves", removing the requirement for LightGBM to be ins…
Browse files Browse the repository at this point in the history
…talled on the system
  • Loading branch information
phil-holland committed Jun 1, 2020
1 parent 96856e8 commit fea2eb4
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 47 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ Whilst the concept behind Impact Rating can in theory be implemented using any b

## Download

> **Important:** CS:GO Impact Rating requires [LightGBM](https://github.com/Microsoft/LightGBM) to be installed on the system. Download the latest binary release from [github.com/Microsoft/LightGBM/releases](https://github.com/Microsoft/LightGBM/releases) and either make sure the executable is added to the system path, or placed in the same directory as the csgo-impact-rating executable.
The latest Impact Rating executable and LightGBM model file (`LightGBM_model.txt`) can be downloaded from this project's release page here: [github.com/Phil-Holland/csgo-impact-rating/releases](https://github.com/Phil-Holland/csgo-impact-rating/releases). These can be placed anywhere on the system - add the parent directory to the system path to access the executable from any location.
The latest Impact Rating executable and LightGBM model file (`LightGBM_model.txt`) can be downloaded from this project's release page here: [github.com/Phil-Holland/csgo-impact-rating/releases](https://github.com/Phil-Holland/csgo-impact-rating/releases). These can be placed together anywhere on the system - add the parent directory to the system path to access the executable from any location.

## Usage

Expand All @@ -85,6 +83,7 @@ the console and a '.rating.json' file.
## Built With

- [demoinfocs-golang](https://github.com/markus-wa/demoinfocs-golang) - used to parse CS:GO demo files
- [leaves](https://github.com/dmitryikh/leaves) - used to process LightGBM models internally
- [pflag](https://github.com/spf13/pflag) - used to build the command line interface
- [pb (v3)](https://github.com/cheggaaa/pb) - used for progress visualisation
- [LightGBM](https://github.com/Microsoft/LightGBM) - used for model training/round outcome prediction
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.14

require (
github.com/cheggaaa/pb/v3 v3.0.4
github.com/dmitryikh/leaves v0.0.0-20200503205002-939b6fa631dd
github.com/markus-wa/demoinfocs-golang/v2 v2.1.0
github.com/spf13/pflag v1.0.5
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dmitryikh/leaves v0.0.0-20200503205002-939b6fa631dd h1:aMtdMlm9R0KQxeQzCLzBbuwA++WlE7owaSoSzcLTySw=
github.com/dmitryikh/leaves v0.0.0-20200503205002-939b6fa631dd/go.mod h1:wzMig9tMIJB8HsxXHppa9yRPo8BpO0eBM/Z4xnaohCQ=
github.com/dustin/go-heatmap v0.0.0-20180603032536-b89dbd73785a/go.mod h1:VBmwC4U3p2SMEKr+/m5j0eby7rmUtSoA5TGLwe6P+3A=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
Expand Down
77 changes: 33 additions & 44 deletions internal/evaluate.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,68 +6,52 @@ import (
"io"
"io/ioutil"
"os"
"os/exec"
"sort"
"strconv"
"strings"
"text/tabwriter"

"github.com/dmitryikh/leaves"
)

// EvaluateDemo processes a .tagged.json file, producing an Impact Rating report which is written to
// the console and a '.rating.json' file
func EvaluateDemo(taggedFilePath string, verbosity int, modelPath string) {
// prepare a csv file
// load in the tagged json
fmt.Printf("Reading contents of json file: \"%s\"\n", taggedFilePath)
jsonRaw, _ := ioutil.ReadFile(taggedFilePath)

var demo Demo
err := json.Unmarshal(jsonRaw, &demo)
if err != nil {
panic(err.Error())
}

// create a temp file to write the csv to
file, err := ioutil.TempFile(".", "temp.*.csv")
if err != nil {
panic(err.Error())
panic(err)
}
defer os.Remove(file.Name())

fmt.Printf("Writing csv to temporary file: \"%s\"\n", file.Name())
output := "roundWinner,aliveCt,aliveT,bombDefused,bombPlanted,meanHealthCt,meanHealthT,meanValueCT,meanValueT,roundTime\n"
for _, tick := range demo.Ticks {
csvLine := makeCSVLine(&tick)
output += csvLine + "\n"
// build the input float slice
cols := 9
input := make([]float64, len(demo.Ticks)*cols)
for idx, tick := range demo.Ticks {
input[idx*cols] = float64(tick.GameState.AliveCT)
input[idx*cols+1] = float64(tick.GameState.AliveT)
input[idx*cols+2] = bToF64(tick.GameState.BombDefused)
input[idx*cols+3] = bToF64(tick.GameState.BombPlanted)
input[idx*cols+4] = float64(tick.GameState.MeanHealthCT)
input[idx*cols+5] = float64(tick.GameState.MeanHealthT)
input[idx*cols+6] = float64(tick.GameState.MeanValueCT)
input[idx*cols+7] = float64(tick.GameState.MeanValueT)
input[idx*cols+8] = float64(tick.GameState.RoundTime)
}
file.WriteString(output)
file.Close()

// create a temp file to write prediction results to
rfile, err := ioutil.TempFile(".", "temp.*.txt")
if err != nil {
panic(err.Error())
}
rfile.Close()
defer os.Remove(rfile.Name())

// invoke lightgbm prediction
cmd := exec.Command("lightgbm", "task=predict", "data=\""+file.Name()+"\"",
"header=true", "label_column=name:roundWinner", "input_model=\""+modelPath+"\"",
"output_result=\""+rfile.Name()+"\"")
fmt.Printf("Running command: %s\n", cmd.String())
stdout, err := cmd.Output()
// load the lightgbm model in using leaves
fmt.Printf("Loading LightGBM model from \"%s\"\n", modelPath)
model, err := leaves.LGEnsembleFromFile(modelPath, true)
if err != nil {
panic(err.Error())
panic(err)
}
fmt.Print(string(stdout))
fmt.Printf("Output results written to temporary txt file: \"%s\"\n", rfile.Name())
fmt.Printf("LightGBM model loaded successfully\n")

// read prediction results in
results, err := ioutil.ReadFile(rfile.Name())
if err != nil {
panic(err.Error())
}
lines := strings.Split(string(results), "\n")
preds := make([]float64, len(demo.Ticks))
model.PredictDense(input, len(demo.Ticks), cols, preds, 0, 1)

var ratingOutput Rating

Expand Down Expand Up @@ -109,10 +93,8 @@ func EvaluateDemo(taggedFilePath string, verbosity int, modelPath string) {
roundsPlayed = tick.ScoreCT + tick.ScoreT + 1
}

pred, err := strconv.ParseFloat(lines[idx], 64)
if err != nil {
panic(err.Error())
}
// get the prediction for this tick
pred := preds[idx]

// amend the prediction if no CTs are alive - certain T win
if tick.GameState.AliveCT == 0 {
Expand Down Expand Up @@ -480,6 +462,13 @@ func EvaluateDemo(taggedFilePath string, verbosity int, modelPath string) {
}
}

func bToF64(b bool) float64 {
if b {
return 1.0
}
return 0.0
}

func makeCSVLine(tick *Tick) string {
roundWinner := strconv.FormatInt(int64(tick.RoundWinner), 10)

Expand Down

0 comments on commit fea2eb4

Please sign in to comment.