Skip to content

Commit

Permalink
Merge pull request #217 from lunakv/multiple-coin-flips
Browse files Browse the repository at this point in the history
Extend coin flip syntax to support multiple flips
  • Loading branch information
Fryyyyy authored Nov 21, 2023
2 parents 45d991d + e94fd79 commit e0ebd96
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 9 deletions.
57 changes: 53 additions & 4 deletions dice.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,58 @@ func validateDice(input string, dice []string) (Roll, error) {
return wellFormedDice, nil
}

func flipCoin() string {
coin := []string{"Heads", "Tails"}
side := coin[rand.Intn(len(coin))]
func flipCoin(input string) string {
coinCount, err := parseCoinCount(input)
if err != nil {
return err.Error()
}

flips := make([]int, coinCount)
for i := 0; i < coinCount; i++ {
flips[i] = rand.Intn(2)
}
return formatFlips(flips, coinCount)
}

func parseCoinCount(input string) (int, error) {
coinMaximum := 50

coins := coinRegex.FindStringSubmatch(input)
if coins[1] == "" {
// no number specified - implicit one flip
return 1, nil
}

numCoins, err := strconv.Atoi(coins[1])
if err != nil {
return 0, errors.New("malformed coin toss")
}

return fmt.Sprintf("%s.", side)
if numCoins > coinMaximum {
return 0, errors.New("malformed coin toss (max count is 50)")
}
if numCoins < 1 {
return 0, errors.New("malformed coin toss (min count is 1)")
}
return numCoins, nil
}

func formatFlips(flips []int, count int) string {
prefix := ""
coinNames := []string{"Heads", "Tails"}
joiner := ", "
if count > 5 {
prefix = fmt.Sprintf("%d coins: ", count)
coinNames = []string{"H", "T"}
joiner = ""
}

formatted := make([]string, len(flips))
for i := 0; i < len(formatted); i++ {
formatted[i] = coinNames[flips[i]]
}

return fmt.Sprintf("%s%s.", prefix, strings.Join(formatted, joiner))
}


32 changes: 30 additions & 2 deletions dice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,41 @@ import (
)

func TestFlipCoin(t *testing.T) {
validOutput := regexp.MustCompile(`(?:Heads|Tails).`)
for i := 1; i < 10; i++ {
result := flipCoin()
validOutput := regexp.MustCompile(`(?:Heads|Tails).`)
result := flipCoin("coin")
if !(validOutput.MatchString(result)) {
t.Errorf(`FAIL: Expected heads or tails in output, but result was \"%s\"`, result)
}
}

validOutput = regexp.MustCompile(`(?:(?:Heads|Tails), ){2}(?:Heads|Tails)\.`)
for i := 1; i < 10; i++ {
result := flipCoin("coin 3")
if !(validOutput.MatchString(result)) {
t.Errorf(`FAIL: Expected list of heads or tails in output, but result was \"%s\"`, result)
}
}

validOutput = regexp.MustCompile(`10 coins: [HT]{10}\.`)
for i := 1; i < 10; i++ {
result := flipCoin("coin 10")
if !(validOutput.MatchString(result)) {
t.Errorf(`FAIL: Expected list of H or T in output, but result was \"%s\"`, result)
}
}

errorMsg := "malformed coin toss (max count is 50)"
result := flipCoin("coin 99")
if result != errorMsg {
t.Errorf(`FAIL: Expected count exceeded error, got "%s"`, result)
}

errorMsg = "malformed coin toss (min count is 1)"
result = flipCoin("coin 0")
if result != errorMsg {
t.Errorf(`FAIL: Expected count subceeded error, got "%s"`, result)
}
}

func TestRollDice(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func printHelp() string {
ret = append(ret, "!uncard/vanguard/plane/scheme <cardname> to bring up normally filtered out cards")
ret = append(ret, "!url <mtr/ipg/cr/jar> to bring up the links to policy documents")
ret = append(ret, "!roll <X> to roll X-sided die; !roll <XdY> to roll X Y-sided dice")
ret = append(ret, "!coin to flip a coin (heads/tails)")
ret = append(ret, "!coin to flip a coin (heads/tails); !coin <X> to flip X coins")
ret = append(ret, "https://github.com/Fryyyyy/Fryatog/issues for bugs & feature requests")
return strings.Join(ret, " · ")
}
Expand Down Expand Up @@ -434,9 +434,9 @@ func handleCommand(params *fryatogParams, c chan string) {
c <- rollDice(message)
return

case message == "coin":
case coinRegex.MatchString(message):
log.Debug("Coin flip")
c <- flipCoin()
c <- flipCoin(message)
return

case ruleRegexp.MatchString(message),
Expand Down
1 change: 1 addition & 0 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var (
wordEndingInBang = regexp.MustCompile(`!["'] |\n+`)
wordStartingWithBang = regexp.MustCompile(`\s+! *\S+`)

coinRegex = regexp.MustCompile(`^coin(?:\s+(\d+))?`)
diceRegex = regexp.MustCompile(`^(?:roll\s+)?(\d*)d(\d+)([+-]\d+)?`)

cardMetadataRegex = regexp.MustCompile(`(?i)^(?:rulings?|reminder|flavou?r) `)
Expand Down

0 comments on commit e0ebd96

Please sign in to comment.