commit f49c9d48e342615f5c327c319b5319c4e12aa457
parent 9766e397b006b38ba94438c7d309e2c7d8a8e27e
Author: sin <sin@2f30.org>
Date: Thu, 2 May 2013 16:47:18 +0100
separate games.go and hangman.go
Diffstat:
M | src/games/games.go | | | 209 | ++++--------------------------------------------------------------------------- |
A | src/games/hangman.go | | | 194 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 204 insertions(+), 199 deletions(-)
diff --git a/src/games/games.go b/src/games/games.go
@@ -1,150 +1,19 @@
package games
import (
- "bytes"
- "fmt"
"irc"
- "strings"
- "time"
)
-// just a function with a gamestate pointer as input
-type hgstateFn func(*hangmanState)
-
-//this is a base gamestate for irc games
+// This is a base gamestate for irc games
type gameState struct {
- ictx *irc.IrcContext // the context to send commands with
- gchan *chan irc.IrcMessage // a chan that intercepts game irc messages
- name string //name of the game
- creator string //name of game creator
-}
-
-//specific state for hangman game
-type hangmanState struct {
- gameState
- lives uint //lives remaining
- tried string //tried characters
- goal string //goal word
- foundnow string //found now chars (_ if not found)
- botname string //name of irc bot for privmsg
- channame string //name of invoking chan for privmsg
- statchan *chan hgstateFn //channel of next game state
-}
-
-//creates a new hangman state with default values
-func newHangmanState(ic *irc.IrcContext, gc *chan irc.IrcMessage, sc *chan hgstateFn) *hangmanState {
- return &hangmanState{gameState{ic, gc, "hangman", "dsp"},
- 5, "", "", "", "unnamed", "", sc}
-}
-
-//for printf style functions, a short status of the game state
-func (s *hangmanState) String() string {
- return fmt.Sprintf(">>[%s]<< lives: %d - tried: [%s]", s.foundnow, s.lives, s.tried)
-}
-
-//First hangman state. Awaits on the game chan for 2 special irc messages to set the nickname
-//of the bot and the channel on which to send msgs. Then it emits the next state (hangmanWaitGoal)
-//to the state channel.
-func hangmanInit(cs *hangmanState) {
- for msg := range *cs.gchan {
- if msg.Command == "SETNICK" {
- cs.botname = msg.Args[0]
- }
- if msg.Command == "SETCHAN" {
- cs.channame = msg.Args[0]
- }
- if cs.botname != "" && cs.channame != "" {
- break
- }
- }
- *cs.statchan <- hangmanWaitGoal
-}
-
-//Second hangman state. It waits on a private query for the goal and then emits the next state (hangmanMainLoop)
-//to the state channel
-func hangmanWaitGoal(cs *hangmanState) {
- cs.ictx.Privmsg(cs.channame, "Hangman is waiting for goal. When ready use hangman <letter|possibleword>")
- for msg := range *cs.gchan {
- msg.Args[1] = strings.TrimSpace(msg.Args[1])
- if msg.Args[0] == cs.botname {
- if strings.HasPrefix(msg.Args[1], "setgoal ") {
- cs.goal = msg.Args[1][8:]
- who := msg.Nick
- cs.ictx.Privmsg(who, "Goal is now set to: "+cs.goal)
- runes := make([]rune, len(cs.goal))
- for i := 0; i < len(runes); i++ {
- runes[i] = '_'
- }
- cs.foundnow = string(runes)
- break
- } else {
- who := msg.Nick
- cs.ictx.Privmsg(who, "Now we are playing hangman. Use setgoal <WORD>")
- }
- } else {
- if strings.HasPrefix(msg.Args[1], "hangman ") {
- cs.ictx.Privmsg(msg.Args[0], "Still waiting for goal...")
- }
- }
- }
- *cs.statchan <- hangmanMainLoop
+ ictx *irc.IrcContext // The context to send commands with
+ gchan *chan irc.IrcMessage // A chan that intercepts game irc messages
+ name string // Name of the game
+ creator string // Name of game creator
}
-//Third hangman state. It waits for hangman prefixed messages on the game channel. If a word is provided
-//and it doesn't match the goal it reduces lives and continues. If it matches the game is won.
-//If a character is provided, it checks on the tried characters and if it is in it continues without losing.
-//If it is not in the tried characters , checks for it with checkLetter(this updates the tried characters)
-//and reprints the game state. At each loop if foundNow matches the goal the game is won.
-//Then it emits the final state (hangmanEnd) to the state channel
-func hangmanMainLoop(cs *hangmanState) {
- cs.ictx.Privmsg(cs.channame, cs.String())
- for msg := range *cs.gchan {
- msg.Args[1] = strings.TrimSpace(msg.Args[1])
- if msg.Args[0] != cs.botname {
- if cs.lives < 1 {
- cs.ictx.Privmsg(msg.Args[0], "You are hanged! Goal was ->"+cs.goal)
- *cs.statchan <- hangmanEnd
- }
- if strings.HasPrefix(msg.Args[1], "hangman ") {
- in := msg.Args[1][8:]
- if len(in) > 1 {
- if in == cs.goal {
- cs.ictx.Privmsg(msg.Args[0], "You won!")
- *cs.statchan <- hangmanEnd
- } else {
- cs.ictx.Privmsg(msg.Args[0], "You lost a life punk...")
- cs.lives -= 1
- cs.ictx.Privmsg(msg.Args[0], cs.String())
- continue
- }
- } else {
- if strings.Index(cs.tried, string(in[0])) != -1 {
- cs.ictx.Privmsg(msg.Args[0], "I heard you the first time")
- continue
- }
- hm := cs.checkLetter([]rune(in)[0])
- cs.ictx.Privmsg(msg.Args[0], fmt.Sprintf("You got %d letters", hm))
- if hm == 0 {
- cs.lives -= 1
- }
- cs.ictx.Privmsg(msg.Args[0], cs.String())
- if cs.foundnow == cs.goal {
- cs.ictx.Privmsg(msg.Args[0], "You won!")
- *cs.statchan <- hangmanEnd
- }
- }
- }
- }
- }
-}
-
-//Fourth hangman state. Final state mostly for cleanup code.
-func hangmanEnd(cs *hangmanState) {
- *cs.statchan <- nil
-}
-
-//A GameEnger will List available games and invoke a new one if it is on the map.
-//New games are invoked as new goroutines that communicate via channels.
+// A GameEnger will List available games and invoke a new one if it is on the map.
+// New games are invoked as new goroutines that communicate via channels.
type GameEnger interface {
List() []string
New(string, *irc.IrcContext, *chan irc.IrcMessage)
@@ -154,37 +23,7 @@ type gameEng struct {
games map[string]func(*irc.IrcContext, *chan irc.IrcMessage)
}
-//Main hangman dispatch, It sets a timeout and then runs as a new goroutine each
-//consecutive state it receives on the statechannel. Before ending it removes
-//the irc Intercept channel for this game.
-func cmdHangman(a *irc.IrcContext, gc *chan irc.IrcMessage) {
- timeout := time.After(4 * time.Minute)
- a.AddIntercept(gc)
- stchan := make(chan hgstateFn, 1)
- hc := newHangmanState(a, gc, &stchan)
- stchan <- hangmanInit
- for {
- select {
- case <-timeout:
- a.Privmsg(hc.channame, "Time is up! Goal was -> "+hc.goal)
- a.DelIntercept(gc)
- close(stchan)
- return
- case st := <-stchan:
- if st != nil {
- go st(hc)
- continue
- } else {
- break
- }
- }
- break
- }
- a.DelIntercept(gc)
- close(stchan)
-}
-
-//Returns all available games as a string array.
+// Returns all available games as a string array
func (g *gameEng) List() []string {
r := []string{}
for k, _ := range g.games {
@@ -193,43 +32,15 @@ func (g *gameEng) List() []string {
return r
}
-//Initializes games strings with their corresponding goroutines
+// Initializes games strings with their corresponding goroutines
func NewGameEng() *gameEng {
return &gameEng{games: map[string]func(*irc.IrcContext, *chan irc.IrcMessage){"hangman": cmdHangman}}
}
-//Fire up a goroutine if the game is in the map
+// Fire up a goroutine if the game is in the map
func (g *gameEng) New(a string, ic *irc.IrcContext, gc *chan irc.IrcMessage) {
if f, ok := g.games[a]; ok {
go f(ic, gc)
}
}
-
-//Checks letter against the goal.
-//returns the number of new letters discovered.
-//returns -1 if repeated check is detected.
-func (g *hangmanState) checkLetter(c rune) int {
- now := []rune(g.foundnow)
- all := []rune(g.goal)
- var buf bytes.Buffer
- for _, v := range g.tried {
- if c == v {
- return -1
- }
- }
- buf.WriteString(g.tried + " " + string(c))
- g.tried = buf.String()
- hm := 0
- for i, v := range all {
- if v == c {
- if now[i] == v {
- return -1
- } /* again */
- now[i] = v
- hm++
- }
- }
- g.foundnow = string(now)
- return hm
-}
diff --git a/src/games/hangman.go b/src/games/hangman.go
@@ -0,0 +1,194 @@
+package games
+
+import (
+ "bytes"
+ "fmt"
+ "irc"
+ "strings"
+ "time"
+)
+
+// Just a function with a gamestate pointer as input
+type hgstateFn func(*hangmanState)
+
+// Specific state for hangman game
+type hangmanState struct {
+ gameState
+ lives uint // Lives remaining
+ tried string // Tried characters
+ goal string // Goal word
+ foundnow string // Found now chars (_ if not found)
+ botname string // Name of irc bot for privmsg
+ channame string // Name of invoking chan for privmsg
+ statchan *chan hgstateFn // Channel of next game state
+}
+
+// Creates a new hangman state with default values
+func newHangmanState(ic *irc.IrcContext, gc *chan irc.IrcMessage, sc *chan hgstateFn) *hangmanState {
+ return &hangmanState{gameState{ic, gc, "hangman", "dsp"},
+ 5, "", "", "", "unnamed", "", sc}
+}
+
+// For printf style functions, a short status of the game state
+func (s *hangmanState) String() string {
+ return fmt.Sprintf(">>[%s]<< lives: %d - tried: [%s]", s.foundnow, s.lives, s.tried)
+}
+
+// First hangman state. Awaits on the game chan for 2 special irc messages to set the nickname
+// of the bot and the channel on which to send msgs. Then it emits the next state (hangmanWaitGoal)
+// to the state channel.
+func hangmanInit(cs *hangmanState) {
+ for msg := range *cs.gchan {
+ if msg.Command == "SETNICK" {
+ cs.botname = msg.Args[0]
+ }
+ if msg.Command == "SETCHAN" {
+ cs.channame = msg.Args[0]
+ }
+ if cs.botname != "" && cs.channame != "" {
+ break
+ }
+ }
+ *cs.statchan <- hangmanWaitGoal
+}
+
+// Second hangman state. It waits on a private query for the goal and then emits the next state (hangmanMainLoop)
+// to the state channel.
+func hangmanWaitGoal(cs *hangmanState) {
+ cs.ictx.Privmsg(cs.channame, "Hangman is waiting for goal. When ready use hangman <letter|possibleword>")
+ for msg := range *cs.gchan {
+ msg.Args[1] = strings.TrimSpace(msg.Args[1])
+ if msg.Args[0] == cs.botname {
+ if strings.HasPrefix(msg.Args[1], "setgoal ") {
+ cs.goal = msg.Args[1][8:]
+ who := msg.Nick
+ cs.ictx.Privmsg(who, "Goal is now set to: "+cs.goal)
+ runes := make([]rune, len(cs.goal))
+ for i := 0; i < len(runes); i++ {
+ runes[i] = '_'
+ }
+ cs.foundnow = string(runes)
+ break
+ } else {
+ who := msg.Nick
+ cs.ictx.Privmsg(who, "Now we are playing hangman. Use setgoal <WORD>")
+ }
+ } else {
+ if strings.HasPrefix(msg.Args[1], "hangman ") {
+ cs.ictx.Privmsg(msg.Args[0], "Still waiting for goal...")
+ }
+ }
+ }
+ *cs.statchan <- hangmanMainLoop
+}
+
+// Third hangman state. It waits for hangman prefixed messages on the game channel. If a word is provided
+// and it doesn't match the goal it reduces lives and continues. If it matches the game is won.
+// If a character is provided, it checks on the tried characters and if it is in it continues without losing.
+// If it is not in the tried characters , checks for it with checkLetter(this updates the tried characters)
+// and reprints the game state. At each loop if foundNow matches the goal the game is won.
+// Then it emits the final state (hangmanEnd) to the state channel.
+func hangmanMainLoop(cs *hangmanState) {
+ cs.ictx.Privmsg(cs.channame, cs.String())
+ for msg := range *cs.gchan {
+ msg.Args[1] = strings.TrimSpace(msg.Args[1])
+ if msg.Args[0] != cs.botname {
+ if cs.lives < 1 {
+ cs.ictx.Privmsg(msg.Args[0], "You are hanged! Goal was ->"+cs.goal)
+ *cs.statchan <- hangmanEnd
+ }
+ if strings.HasPrefix(msg.Args[1], "hangman ") {
+ in := msg.Args[1][8:]
+ if len(in) > 1 {
+ if in == cs.goal {
+ cs.ictx.Privmsg(msg.Args[0], "You won!")
+ *cs.statchan <- hangmanEnd
+ } else {
+ cs.ictx.Privmsg(msg.Args[0], "You lost a life punk...")
+ cs.lives -= 1
+ cs.ictx.Privmsg(msg.Args[0], cs.String())
+ continue
+ }
+ } else {
+ if strings.Index(cs.tried, string(in[0])) != -1 {
+ cs.ictx.Privmsg(msg.Args[0], "I heard you the first time")
+ continue
+ }
+ hm := cs.checkLetter([]rune(in)[0])
+ cs.ictx.Privmsg(msg.Args[0], fmt.Sprintf("You got %d letters", hm))
+ if hm == 0 {
+ cs.lives -= 1
+ }
+ cs.ictx.Privmsg(msg.Args[0], cs.String())
+ if cs.foundnow == cs.goal {
+ cs.ictx.Privmsg(msg.Args[0], "You won!")
+ *cs.statchan <- hangmanEnd
+ }
+ }
+ }
+ }
+ }
+}
+
+// Fourth hangman state. Final state mostly for cleanup code.
+func hangmanEnd(cs *hangmanState) {
+ *cs.statchan <- nil
+}
+
+// Main hangman dispatch, It sets a timeout and then runs as a new goroutine each
+// consecutive state it receives on the statechannel. Before ending it removes
+// the irc Intercept channel for this game.
+func cmdHangman(a *irc.IrcContext, gc *chan irc.IrcMessage) {
+ timeout := time.After(4 * time.Minute)
+ a.AddIntercept(gc)
+ stchan := make(chan hgstateFn, 1)
+ hc := newHangmanState(a, gc, &stchan)
+ stchan <- hangmanInit
+ for {
+ select {
+ case <-timeout:
+ a.Privmsg(hc.channame, "Time is up! Goal was -> "+hc.goal)
+ a.DelIntercept(gc)
+ close(stchan)
+ return
+ case st := <-stchan:
+ if st != nil {
+ go st(hc)
+ continue
+ } else {
+ break
+ }
+ }
+ break
+ }
+ a.DelIntercept(gc)
+ close(stchan)
+}
+
+// Checks letter against the goal.
+// Returns the number of new letters discovered.
+// Returns -1 if repeated check is detected.
+func (g *hangmanState) checkLetter(c rune) int {
+ now := []rune(g.foundnow)
+ all := []rune(g.goal)
+ var buf bytes.Buffer
+ for _, v := range g.tried {
+ if c == v {
+ return -1
+ }
+ }
+ buf.WriteString(g.tried + " " + string(c))
+ g.tried = buf.String()
+ hm := 0
+ for i, v := range all {
+ if v == c {
+ if now[i] == v {
+ return -1
+ } /* again */
+ now[i] = v
+ hm++
+ }
+ }
+ g.foundnow = string(now)
+ return hm
+}