-
Notifications
You must be signed in to change notification settings - Fork 0
/
bash-n-client
182 lines (158 loc) · 3.78 KB
/
bash-n-client
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/bin/bash
# bash-n-client - bash library for connected-n game clients
#
# Copyright (C) 2019, 2020 Christian Garbs <mitch@cgarbs.de>
# Licensed under GNU GPL v3 or later.
#
# This file is part of bash-n.
#
# bash-n is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# bash-n is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with bash-n. If not, see <http://www.gnu.org/licenses/>.
############################
# overwrite these hooks with your own strategy
# gets called when it is our turn
# RETURN the column number [0-6] of your move
calculate_move()
{
_die "no implementation of calculate_move() - overwrite this function!"
}
# gets called when the server has received a move in the current game
# $1 CLIENT NAME that made the move
# $2 COLUMN [0-6] where the move was made
record_move()
{
_die "no implementation of record_move() - overwrite this function!"
}
# gets called when a new game starts - use this to init or reset game state
# $1 CLIENT NAME of our opponent
game_start()
{
_die "no implementation of game_start() - overwrite this function!"
}
# gets called when a game ends
# $1 RESULT is one of [WIN|DRAW|LOSE]
# $2 CLIENT NAME that scored the result
# $3 REASON of the result
game_end()
{
_die "no implementation of game_end() - overwrite this function!"
}
############################
bash_n_state=UNDEFINED
# $* MESSAGE
_die()
{
echo "$@"
exit 1
}
# $1 COMMAND to send
# $* ARGUMENTS to send
_send()
{
local IFS=';'
echo "$*" >&3
}
_on_connected()
{
bash_n_state=CONNECTED
}
# $1 SEASON HASH
_on_new_season()
{
local hash=$1
bash_n_state=SEASON
_send JOIN "$hash"
}
# $1 OPPONENT
_on_new_game()
{
local opponent=$1
bash_n_state=GAME
game_start "$opponent"
}
# $1 TURN HASH
_on_execute_turn()
{
local hash=$1
calculate_move
# shellcheck disable=SC2317
_send INSERT $? "$hash"
}
# $1 RESULT is one of [WIN|DRAW|LOSE]
# $2 CLIENT NAME that scored the result
# $3 REASON of the result
_on_game_result()
{
local result=$1 who=$2 why=$3
bash_n_state=SEASON
game_end "$result" "$who" "$why"
}
# $1 CLIENT NAME reveived from server
# $2 CLIENT NAME sent to server
_check_client_name()
{
local actual=$1 expected=$2
if [ "$actual" != "$expected" ]; then
_die "server sent WELCOME with bot name <$actual> instead of <$expected>"
fi
}
# set up filedescriptors:
# 0 is input from server via UDP
# 1 is the tty if it can be determined, regular stderr otherwise
# 2 is regular stderr
# 3 is output to server via UDP
_setup_redirects()
{
exec 3>&1
if tty -s 2>/dev/null; then
exec 1>"$(tty)"
else
exec 1>&2
fi
}
# $1 NAME of the client
start_client()
{
local name=$1
_setup_redirects
bash_n_state=CONNECTING
_send REGISTER "$name"
while IFS=\; read -ra line; do
local cmd=${line[0]}
case "$bash_n_state-$cmd" in
CONNECTING-WELCOME)
_check_client_name "${line[1]}" "$name"
_on_connected
;;
CONNECTED-NEW\ SEASON) ;&
SEASON-NEW\ SEASON)
_on_new_season "${line[1]}"
;;
SEASON-NEW\ GAME)
_on_new_game "${line[1]}"
;;
GAME-YOURTURN)
_on_execute_turn "${line[1]}"
;;
GAME-TOKEN\ INSERTED)
record_move "${line[1]}" "${line[2]}"
;;
GAME-RESULT)
_on_game_result "${line[1]}" "${line[2]}" "${line[3]}"
;;
*)
_die "received unknown command <$cmd> in state <$bash_n_state>"
;;
esac
done
}