-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplay.js
318 lines (288 loc) · 7.4 KB
/
play.js
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
const POLLING_SEC = 5 * 1000; // 5 seconds
const MSG_ENTER_ANS = "Enter SECRET unique 4 digit number.";
const MSG_SECRET_ADDED = "SECRET added, Please wait..";
const MSG_YOUR_TURN = "YOUR TURN!";
const MSG_PLZ_WAIT = "Please wait..";
const MSG_YOU_WIN = "YOU WIN!";
const MSG_YOU_LOSE = "YOU LOSE!";
const MSG_DRAW_GAME = "DRAW!";
//const HINT_FULL = "O";
//const HINT_HALF = "X";
//const HINT_NONE = "-";
const HINT_FULL = "<img class='mx-1' height='24' src='./img/heart_full.png'>";
const HINT_HALF = "<img class='mx-1' height='24' src='./img/heart_half.png'>";
const HINT_NONE = "<img class='mx-1' height='24' src='./img/heart_blank.png'>";
// ---------- MAIN ----------
// game config
let api = null;
let room_data = null;
let room_no = get_qs('r');
let player_no = +get_qs('p');
// validate room no
if (!room_no) {
location.href = './index.html';
}
else {
// update api url
api = get_api_url(room_no);
// join room
join_room();
// handle inputs
$('body').on('keyup', '.input-num', handle_input);
$('body').on('click', '.btn-send', handle_send);
$('body').on('click', '.btn-exit', handle_exit);
}
// ---------- FUNCTIONS ----------
// input
function validate_number(num) {
return !!num.match(/^\d{4}$/) && // 4 digit number
!(/([0-9]).*?\1/).test(num); // not duplicate
}
function handle_input(evt) {
let $input = $(evt.target);
let num = $input.val();
let valid = validate_number(num);
if (valid) {
$input.removeClass('is-error');
$input.addClass('is-success');
}
else {
$input.removeClass('is-success');
$input.addClass('is-error');
}
}
function handle_send(evt) {
let $input = $('.input-num');
let num = $input.val();
// validate number
let valid = validate_number(num);
if (!valid) return;
// remove value from input
$input.val('');
// (1) input player answer
let answer = get_player_answer();
if (!answer) {
set_info_msg(MSG_SECRET_ADDED);
set_player_answer(num, _ => sync_room_data(polling));
}
// (2) guess friend number
else {
let turn = get_player_number().length;
let hint = create_hint(get_friend_answer(), num);
add_log_msg(turn+1, num, hint);
set_player_number(num);
sync_room_data(polling);
}
// switch to friend turn
friend_turn();
}
function handle_exit(evt) {
// empty room
ajax_post(api, null, _ => {
location.href = './index.html';
});
}
// api
function fetch_room_data(callback) {
$.ajax(api).done(resp => {
// update global room data
room_data = resp.data || get_default_data();
// callback
callback(room_data);
});
}
function sync_room_data(callback=null) {
room_data.ts = now();
ajax_post(api, room_data, callback);
}
// data
function set_player_answer(num, callback) {
fetch_room_data(_ => {
room_data.answer[player_no-1] = num;
callback();
});
}
function get_player_answer() {
return room_data.answer[{ 1: 0, 2: 1 }[player_no]];
}
function get_friend_answer() {
return room_data.answer[{ 1: 1, 2: 0 }[player_no]];
}
function set_player_number(num) {
return room_data.number[player_no-1].push(num);
}
function get_player_number() {
return room_data.number[player_no-1];
}
// gameplay
function join_room() {
fetch_room_data(_ => {
// update online player
room_data.online += 1;
player_no = player_no || room_data.online;
// reject if room full
if (player_no > 2) {
location.href = './index.html';
return;
}
// sync data
sync_room_data();
// update ui
$('.label-player').html(`Player ${player_no}`);
$('.label-room').html(`Room ${room_no}`);
// (1) enter secret
let answer = get_player_answer();
if (!answer) {
set_info_msg(MSG_ENTER_ANS);
your_turn();
}
// (2) continue play
else {
// start msg
set_info_msg(MSG_SECRET_ADDED);
// restore log
let friend_ans = get_friend_answer();
get_player_number().forEach((num, turn) => {
let hint = create_hint(get_friend_answer(), num);
add_log_msg(turn+1, num, hint);
});
// flow
setTimeout(polling, POLLING_SEC);
friend_turn();
}
});
}
function polling() {
fetch_room_data(_ => {
// both players enter their number
if (!ready2play()) {
setTimeout(polling, POLLING_SEC);
}
// ready to play
else {
// game over
if (check_game_over()) {
game_over();
}
// your turn
else if (check_your_turn()) {
your_turn();
}
// friend turn -> polling
else {
setTimeout(polling, POLLING_SEC);
}
}
});
}
function ready2play() {
return room_data.answer.every(r => !!r);
}
function your_turn() {
if (ready2play())
add_html_msg(MSG_YOUR_TURN);
$('.btn-send').show();
$('.btn-wait').hide();
$('.btn-exit').hide();
$('.input-num').focus();
}
function friend_turn() {
if (ready2play())
add_html_msg(MSG_PLZ_WAIT);
$('.btn-send').hide();
$('.btn-wait').show();
$('.btn-exit').hide();
}
function game_over() {
// show message
let wins = check_win_status();
if (wins.every(r => r)) { // draw
add_success_msg(MSG_DRAW_GAME);
}
else { // win or lose
let win = wins[player_no-1];
win ? add_success_msg(MSG_YOU_WIN)
: add_error_msg(MSG_YOU_LOSE);
}
$('.btn-send').hide();
$('.btn-wait').hide();
$('.btn-exit').show();
}
function check_your_turn() {
let p1_size = room_data.number[0].length;
let p2_size = room_data.number[1].length;
if (player_no == 1) {
return p1_size == p2_size;
}
else if (player_no == 2) {
return p1_size > p2_size;
}
return false;
}
function check_win_status() {
let [p1_ans, p2_ans] = room_data.answer;
let [p1_num, p2_num] = room_data.number;
// turn not complete
if (p1_num.length != p2_num.length)
return [false, false];
let p1_win = p1_num.indexOf(p2_ans) > -1;
let p2_win = p2_num.indexOf(p1_ans) > -1;
return [ p1_win, p2_win ];
}
function check_game_over() {
return check_win_status().some(s => s);
}
function create_hint(ans, num) {
// prepare data
let pad_ans = String(ans).padStart(4, '0');
let pad_num = String(num).padStart(4, '0');
let left_ans = [];
let left_num = [];
// find match
let count_match = 0;
for (let i=0; i<pad_ans.length; i++) {
let a = pad_ans[i];
let n = pad_num[i];
if (a == n) {
count_match += 1;
}
else {
left_ans.push(a);
left_num.push(n);
}
}
// find found
let count_found = 0;
left_num.forEach(n => {
let idx = left_ans.indexOf(n);
if (idx > -1) {
count_found += 1;
left_ans.splice(idx, 1);
}
});
// else dash
let count_else = 4 - count_match - count_found;
// return
return HINT_FULL.repeat(count_match)
+ HINT_HALF.repeat(count_found)
+ HINT_NONE.repeat(count_else);
}
// message
function add_msg(html) { $('.record.container').prepend(html); }
function add_html_msg(msg, clz=null) { mario_say(msg, clz); }
function add_success_msg(msg) { add_html_msg(msg, 'success'); }
function add_error_msg(msg) { add_html_msg(msg, 'danger'); }
function add_info_msg(msg) { add_html_msg(msg, '_primary'); }
function set_info_msg(msg) { add_info_msg(msg); }
function add_log_msg(turn, num, hint) {
let html = `
<div class='row'>
<div class='col-3'>[${turn}]</div>
<div class='col-3'>${num}</div>
<div class='col-6'>${hint}</div>
</div>`;
add_msg(html);
}
function mario_say(msg, clz) {
$('.msg-mario').html(`<span class='text-${clz}'>${msg}</span>`);
}