mirror of
https://github.com/sbrow/risk-multilang.git
synced 2025-12-29 16:37:39 -05:00
131 lines
2.4 KiB
Go
131 lines
2.4 KiB
Go
package risk
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/sbrow/prob/dice"
|
|
)
|
|
|
|
const Delim = ","
|
|
|
|
// loadRolls loads a roll table from path
|
|
func loadRolls(n int) []string {
|
|
d := make([]dice.Die, n)
|
|
for i := 0; i < n; i++ {
|
|
d[i] = dice.D6()
|
|
}
|
|
rolls, err := dice.NewTable(d)
|
|
if err != nil {
|
|
panic(err) // TODO(sbrow): Fix
|
|
}
|
|
var out []string
|
|
for _, s := range rolls.Data {
|
|
out = append(out, s[0])
|
|
}
|
|
return out
|
|
}
|
|
|
|
func sortRoll(s string) string {
|
|
i := strToInt(s)
|
|
sort.Sort(sort.Reverse(sort.IntSlice(i)))
|
|
s = fmt.Sprint(i[0], s[1:])
|
|
for j := 2; j <= len(s)-1; j += 2 {
|
|
s = fmt.Sprint(s[:j], i[j/2], s[j+1:])
|
|
}
|
|
s = fmt.Sprint(s[:len(s)-1], i[len(i)-1])
|
|
return s
|
|
}
|
|
|
|
// Compare enumerates the results of a combat.
|
|
//
|
|
// TODO(sbrow): Rename
|
|
func Compare(atk, def int, mods ...string) (win, tie, loss float64) {
|
|
if atk < 1 || def < 1 {
|
|
return
|
|
}
|
|
sum := 0.0
|
|
for _, atkRoll := range loadRolls(min(atk, 3)) {
|
|
atkRoll = sortRoll(atkRoll)
|
|
for _, defRoll := range loadRolls(min(def, 2)) {
|
|
defRoll = sortRoll(defRoll)
|
|
lost := Combat(atkRoll, defRoll, mods...)
|
|
switch {
|
|
case lost == 0:
|
|
win++
|
|
case lost == 1:
|
|
if atk > 1 && def > 1 {
|
|
tie++
|
|
} else {
|
|
loss++
|
|
}
|
|
case lost == 2:
|
|
loss++
|
|
}
|
|
sum++
|
|
}
|
|
}
|
|
|
|
return win / sum, tie / sum, loss / sum
|
|
}
|
|
|
|
// strToInt converts a string value to a list of ints,
|
|
// using rolltable.Delim as the delimiter.
|
|
func strToInt(s string) []int {
|
|
i := []int{}
|
|
for _, t := range strings.Split(s, dice.Delim) {
|
|
n, err := strconv.Atoi(t)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
i = append(i, n)
|
|
}
|
|
return i
|
|
}
|
|
|
|
// Combat determines the winner of a dice clash.
|
|
func Combat(atk, def string, mod ...string) int {
|
|
a, d := strToInt(atk), strToInt(def)
|
|
addModifiers(&a, &d, mod...)
|
|
|
|
short := min(len(a), len(d))
|
|
|
|
lost := 0
|
|
for i := 0; i < short; i++ {
|
|
if d[i] >= a[i] {
|
|
lost++
|
|
}
|
|
}
|
|
return lost
|
|
}
|
|
|
|
// addModifiers adds modifiers to the dice roll
|
|
func addModifiers(atk, def *[]int, mod ...string) {
|
|
sort.Ints(*atk)
|
|
sort.Ints(*def)
|
|
if len(*atk) > len(*def) {
|
|
*atk = (*atk)[len(*def)-1:]
|
|
}
|
|
for _, m := range mod {
|
|
switch m {
|
|
case "fortification":
|
|
if len(*def) == 2 {
|
|
(*def)[1] = min((*def)[1]+1, 6)
|
|
}
|
|
fallthrough
|
|
case "bunker":
|
|
(*def)[0] = min((*def)[0]+1, 6)
|
|
case "ammo shortage":
|
|
i := 0
|
|
if len(*def) == 2 {
|
|
i = 1
|
|
}
|
|
(*def)[i] = max((*def)[1]-1, 0)
|
|
}
|
|
}
|
|
sort.Sort(sort.Reverse(sort.IntSlice(*atk)))
|
|
sort.Sort(sort.Reverse(sort.IntSlice(*def)))
|
|
}
|