add countdown goroutine, new commands, and more
This commit is contained in:
parent
74e4d33934
commit
b2065f86a7
117
main.go
117
main.go
|
@ -8,7 +8,13 @@ package main
|
|||
If the counter hits zero, the button is frozen and nobody can press it any more.
|
||||
|
||||
firstly, we need to import all the libraries we need.
|
||||
we need fmt for debugging. We need net for connecting to IRC. We need bufio for reading from the network socket we create to connect to IRC with. We need strings for splitting and testing strings and regexp to help parse messages.
|
||||
* fmt for debugging and formatting IRC messages
|
||||
* net for connecting to IRC
|
||||
* bufio for reading from the network socket we create to connect to IRC with
|
||||
* strings for splitting and testing strings
|
||||
* regexp to help parse messages.
|
||||
* sync to make things concurrency safe
|
||||
* time to sleep for some time before reducing the counter
|
||||
*/
|
||||
import (
|
||||
"bufio"
|
||||
|
@ -16,17 +22,33 @@ import (
|
|||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
as we'll be referencing them a lot later, we should create some constants to store things like the network address and our IRC nick/user name. This is set up for connecting to tilde.town from localhost.
|
||||
we also set the minimum and maximum timeout lengths (in seconds) for the counter to tick down, and the value to reset to.
|
||||
*/
|
||||
const (
|
||||
nick string = "thebuttonbot"
|
||||
user string = "thebuttonbot"
|
||||
realname string = "thebuttonbot"
|
||||
channel string = "#thebutton"
|
||||
server string = "localhost:6667"
|
||||
nick string = "thebuttonbot"
|
||||
user string = "thebuttonbot"
|
||||
realname string = "thebuttonbot"
|
||||
channel string = "#thebutton"
|
||||
server string = "localhost:6667"
|
||||
maxTime int = 10
|
||||
minTime int = 5
|
||||
resetCount int = 10
|
||||
)
|
||||
|
||||
/*
|
||||
game state variables. counter is the current count, scores is the scoreboard, and the two mutexes are for protecting the outgoing connection and the counter.
|
||||
*/
|
||||
var (
|
||||
counter int = resetCount
|
||||
scores map[string]int
|
||||
countMutex sync.Mutex
|
||||
connMutex sync.Mutex
|
||||
)
|
||||
|
||||
// IRCMessage is a type that contains the contents of an incoming IRC message.
|
||||
|
@ -83,9 +105,62 @@ func parseMessage(s string) IRCMessage {
|
|||
|
||||
}
|
||||
|
||||
// sendMessage sends a message using connection conn on IRC channel c with content s
|
||||
/*
|
||||
function sendMessage sends a message using connection conn on IRC channel c with content s.
|
||||
It first locks the connection mutex, and unlocks it afterwards, to make the function concurrency-safe.
|
||||
When the mutex is locked by one goroutine, another cannot write to it, preventing a race condition.
|
||||
(We don't need this for recieving messages, as only the main routine recieves messages)
|
||||
*/
|
||||
func sendMessage(conn *net.Conn, c string, s string) {
|
||||
connMutex.Lock()
|
||||
fmt.Fprintf(*conn, "PRIVMSG %s :%s\r\n", c, s)
|
||||
connMutex.Unlock()
|
||||
}
|
||||
|
||||
// function setCounter sets the counter variable to a value, concurrency-safely using a mutex.
|
||||
func setCounter(value int) {
|
||||
countMutex.Lock()
|
||||
counter = value
|
||||
countMutex.Unlock()
|
||||
}
|
||||
|
||||
// function getCounter gets the current value of the counter in a concurrency-safe way.
|
||||
func getCounter() int {
|
||||
var c int
|
||||
countMutex.Lock()
|
||||
c = counter
|
||||
countMutex.Unlock()
|
||||
return c
|
||||
}
|
||||
|
||||
/*
|
||||
function countdown is a goroutine that starts a timer and counts down on the countdown when the timer times out.
|
||||
if it gets a message over the reset channel, the timer and counter are reset.
|
||||
when the timer would hit 0, the locked variable is set. If the timer is locked, timeouts are ignored until it's reset.
|
||||
*/
|
||||
func countdown(conn *net.Conn, reset chan int) {
|
||||
locked := false
|
||||
for {
|
||||
timer := time.NewTimer(time.Duration(maxTime) * time.Second) // TODO randomise a bit, longer times
|
||||
select {
|
||||
case <-timer.C: // on a timer timeout
|
||||
if !locked {
|
||||
c := getCounter()
|
||||
setCounter(c - 1)
|
||||
if c-1 == 0 {
|
||||
locked = true
|
||||
sendMessage(conn, channel, "Ker-Chunk! The timer ticked down to the final position. You're all dead.")
|
||||
} else {
|
||||
sendMessage(conn, channel, fmt.Sprintf("*tick, tick, tick* The timer ticks down. It now reads %d", c-1))
|
||||
}
|
||||
}
|
||||
|
||||
case <-reset:
|
||||
timer.Stop()
|
||||
setCounter(resetCount)
|
||||
locked = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -117,7 +192,9 @@ func main() {
|
|||
fmt.Fprintf(conn, "USER %s 0 * :%s\r\n", user, realname)
|
||||
fmt.Fprintf(conn, "NICK %s\r\n", nick)
|
||||
fmt.Fprintf(conn, "JOIN %s\r\n", channel)
|
||||
|
||||
// Run the goroutine that does the countdown. Create channels we can use to reset it or press the button.
|
||||
reset := make(chan int)
|
||||
go countdown(&conn, reset)
|
||||
/*
|
||||
loop every time there is a new line sent over the connection (a new IRC message/command).
|
||||
We can get the content of it with scanner.Text().
|
||||
|
@ -144,10 +221,11 @@ func main() {
|
|||
because of my dumb message parser, the contents are spread throughout the parameters array.
|
||||
However, we only need to look at the first bit, because we're just reading some set commands:
|
||||
,scores: prints the scoreboard (when it's implemented)
|
||||
,press: press the button, resetting the countdown (when that's implemented)
|
||||
,press: press the button, resetting the countdown and scoring points, as long as it's not locked (when that's implemented)
|
||||
,ping: sends a "pong" as a PRIVMSG to the channel.
|
||||
,countdown: sends the current countdown value
|
||||
,count: sends the current countdown value
|
||||
,help: prints the things you can do
|
||||
,reset: resets the game unconditionally
|
||||
these only matter if they're sent in the bot's channel.
|
||||
scores might get an argument sometime idk
|
||||
TODO implement all these
|
||||
|
@ -164,13 +242,24 @@ func main() {
|
|||
// TODO implement actual game
|
||||
sendMessage(&conn, channel, "Not implemented yet")
|
||||
} else if msgContent == ",press" {
|
||||
sendMessage(&conn, channel, "Not implemented yet")
|
||||
} else if msgContent == ",countdown" {
|
||||
sendMessage(&conn, channel, "Not implemented yet")
|
||||
// TODO score these
|
||||
c := getCounter()
|
||||
if c != 0 {
|
||||
reset <- 0
|
||||
sendMessage(&conn, channel, fmt.Sprintf("You press the button. The countdown resets to %d", resetCount))
|
||||
} else {
|
||||
sendMessage(&conn, channel, "The timer is locked! You can't press the button.")
|
||||
}
|
||||
} else if msgContent == ",reset" {
|
||||
// TODO maybe check
|
||||
reset <- 0
|
||||
sendMessage(&conn, channel, "Timer reset!")
|
||||
} else if msgContent == ",count" {
|
||||
sendMessage(&conn, channel, fmt.Sprintf("You look up at the ominous countdown. It reads %d", getCounter()))
|
||||
} else if msgContent == ",help" {
|
||||
sendMessage(&conn, channel, ",scores: prints current scores")
|
||||
sendMessage(&conn, channel, ",press: press the button! resets the countdown, adds to your score. The lower the count, the higher your score!")
|
||||
sendMessage(&conn, channel, ",countdown: prints current countdown position")
|
||||
sendMessage(&conn, channel, ",count: prints current countdown position")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue