add countdown goroutine, new commands, and more

This commit is contained in:
Nico 2021-03-07 12:03:39 +00:00
parent 74e4d33934
commit b2065f86a7
1 changed files with 103 additions and 14 deletions

117
main.go
View File

@ -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")
}
}
}