/
server.go
120 lines (107 loc) · 2.34 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package main
import (
"bufio"
"errors"
"fmt"
"math/rand"
"net"
"net/textproto"
"sync/atomic"
"github.com/kruszczynski/studies/stats"
)
type command int
const (
rock command = iota
paper
scissors
getStats
quit
)
var commandNames = [5]string{
"ROCK",
"PAPER",
"SCISSORS",
"STATS",
"QUIT",
}
var errInvalidCommand = errors.New("INV")
func main() {
server()
}
func server() {
ln, err := net.Listen("tcp", ":1983")
if err != nil {
fmt.Println(err)
return
}
collector := stats.NewCollector()
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println(err)
continue
}
go handleServerConnection(conn, collector)
}
}
func handleServerConnection(conn net.Conn, collector *stats.Collector) {
// greet the player
conn.Write([]byte("Welcome stranger\n"))
// receive the message
r := bufio.NewReader(conn)
textReader := textproto.NewReader(r)
for {
usedCommand, err := extractCommand(textReader)
if err != nil {
if err == errInvalidCommand {
conn.Write([]byte("INVALID COMMAND\n"))
continue
} else {
conn.Write([]byte("SOMEWHAT FATAL\n"))
break
}
}
handleCommand(usedCommand, conn, collector)
}
}
func extractCommand(reader *textproto.Reader) (command, error) {
line, err := reader.ReadLine()
if err != nil {
return getStats, err
}
for index, commandName := range commandNames {
if commandName == line {
return command(index), nil
}
}
return getStats, errInvalidCommand
}
func handleCommand(usedCommand command, conn net.Conn, collector *stats.Collector) {
if int(usedCommand) < 3 {
// Let's play
response := command(rand.Intn(3))
responseString := commandNames[int(response)]
switch {
// DRAW
case usedCommand == response:
responseString = "DRAW " + responseString
atomic.AddInt32(&collector.DrawsCount, 1)
// USER LOSES
case (usedCommand == 0 && response == 1) ||
(usedCommand == 1 && response == 2) ||
(usedCommand == 2 && response == 0):
responseString = "LOSE " + responseString
atomic.AddInt32(&collector.LossesCount, 1)
// USER WINS, THE DEFAULT
default:
responseString = "WIN " + responseString
atomic.AddInt32(&collector.WinsCount, 1)
}
conn.Write([]byte(responseString + "\n"))
} else if usedCommand == getStats {
conn.Write([]byte(collector.PrintStats() + "\n"))
} else {
conn.Write([]byte("Until then stranger\n"))
conn.Close()
}
}