drawful

drawing game
git clone git://ce9e.org/drawful

commit
7c141fd141b2eea40bd537e4f8ae8c9b8922acca
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2020-12-17 16:43
init

Diffstat

A canvas.js 22 ++++++++++++++++++++++
A drawful.js 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A html.js 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A index.html 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A style.css 18 ++++++++++++++++++
A utils.js 14 ++++++++++++++

6 files changed, 327 insertions, 0 deletions


diff --git a/canvas.js b/canvas.js

@@ -0,0 +1,22 @@
   -1     1 var canvas = document.querySelector('canvas');
   -1     2 canvas.width = 16;
   -1     3 canvas.height = 16;
   -1     4 var ctx = canvas.getContext('2d');
   -1     5 
   -1     6 canvas.addEventListener('mousemove', function(event) {
   -1     7 	if (!(event.buttons & 1)) return;
   -1     8 
   -1     9 	var x = Math.floor((event.clientX - canvas.offsetLeft) / canvas.clientWidth * 16);
   -1    10 	var y = Math.floor((event.clientY - canvas.offsetTop) / canvas.clientHeight * 16);
   -1    11 
   -1    12 	ctx.fillStyle = 'black';
   -1    13 	ctx.fillRect(x, y, 1, 1);
   -1    14 });
   -1    15 
   -1    16 export var clear = function() {
   -1    17 	ctx.clearRect(0, 0, canvas.width, canvas.height);
   -1    18 };
   -1    19 
   -1    20 export var toDataURL = function() {
   -1    21 	return canvas.toDataURL();
   -1    22 };

diff --git a/drawful.js b/drawful.js

@@ -0,0 +1,154 @@
   -1     1 import * as canvas from './canvas.js';
   -1     2 import * as html from './html.js';
   -1     3 import * as utils from './utils.js';
   -1     4 
   -1     5 if (!location.hash) {
   -1     6 	location.hash = utils.randomString(10);
   -1     7 }
   -1     8 
   -1     9 var url = 'https://via.ce9e.org/msg/drawful/' + location.hash.slice(1);
   -1    10 
   -1    11 var state = {
   -1    12 	name: null,
   -1    13 	scores: {},
   -1    14 	host: null,
   -1    15 
   -1    16 	// used only by host
   -1    17 	word: null,
   -1    18 	lies: {},
   -1    19 	picks: {},
   -1    20 };
   -1    21 
   -1    22 var send = function(cmd, arg) {
   -1    23 	return fetch(url, {
   -1    24 		method: 'POST',
   -1    25 		body: JSON.stringify([cmd, state.name, arg]),
   -1    26 	});
   -1    27 };
   -1    28 
   -1    29 var resetRound = function() {
   -1    30 	state.host = null;
   -1    31 	state.word = null;
   -1    32 	state.lies = {};
   -1    33 	state.picks = {};
   -1    34 	canvas.clear();
   -1    35 	document.querySelectorAll('form').forEach(el => {
   -1    36 		el.reset();
   -1    37 		html.disableForm(el, false);
   -1    38 	});
   -1    39 	html.updateDoneList('#lie-done', []);
   -1    40 	html.updateDoneList('#pick-done', []);
   -1    41 };
   -1    42 
   -1    43 var randomWord = function() {
   -1    44 	return 'banana';
   -1    45 };
   -1    46 
   -1    47 var score = function() {
   -1    48 	var player, pick, lier;
   -1    49 
   -1    50 	state.scores[state.host] = state.scores[state.host] || 0;
   -1    51 
   -1    52 	for (player in state.picks) {
   -1    53 		state.scores[player] = state.scores[player] || 0;
   -1    54 		pick = state.picks[player];
   -1    55 		if (pick === state.word) {
   -1    56 			state.scores[player] += 100;
   -1    57 			state.scores[state.host] += 100;
   -1    58 		} else {
   -1    59 			for (lier in state.lies) {
   -1    60 				if (pick === state.lies[lier] && player !== lier) {
   -1    61 					state.scores[lier] += 50;
   -1    62 				}
   -1    63 			}
   -1    64 		}
   -1    65 	}
   -1    66 };
   -1    67 
   -1    68 document.querySelector('#name').addEventListener('submit', function(event) {
   -1    69 	event.preventDefault();
   -1    70 	state.name = event.target.username.value;
   -1    71 	html.setView('scores');
   -1    72 });
   -1    73 
   -1    74 document.querySelector('#scores').addEventListener('submit', function(event) {
   -1    75 	event.preventDefault();
   -1    76 	send('claimHost');
   -1    77 });
   -1    78 
   -1    79 document.querySelector('#draw-host').addEventListener('submit', function(event) {
   -1    80 	event.preventDefault();
   -1    81 	state.img = canvas.toDataURL();
   -1    82 	send('setImage', state.img);
   -1    83 	html.setView('lie-host');
   -1    84 });
   -1    85 
   -1    86 document.querySelector('#lie').addEventListener('submit', function(event) {
   -1    87 	event.preventDefault();
   -1    88 	send('setLie', event.target.word.value);
   -1    89 	html.disableForm(event.target, true);
   -1    90 });
   -1    91 
   -1    92 document.querySelector('#lie-host').addEventListener('submit', function(event) {
   -1    93 	event.preventDefault();
   -1    94 	var options = [state.word];
   -1    95 	for (var player in state.lies) {
   -1    96 		options.push(state.lies[player]);
   -1    97 	}
   -1    98 	send('setOptions', utils.shuffle(options));
   -1    99 	html.setView('pick-host');
   -1   100 });
   -1   101 
   -1   102 document.querySelector('#pick').addEventListener('submit', function(event) {
   -1   103 	event.preventDefault();
   -1   104 	send('setPick', event.target.pick.value);
   -1   105 	html.disableForm(event.target, true);
   -1   106 });
   -1   107 
   -1   108 document.querySelector('#pick-host').addEventListener('submit', function(event) {
   -1   109 	event.preventDefault();
   -1   110 	score();
   -1   111 	send('setScores', state.scores);
   -1   112 	html.setView('scores');
   -1   113 	resetRound();
   -1   114 });
   -1   115 
   -1   116 var evtSource = new EventSource(url);
   -1   117 evtSource.onmessage = function(event) {
   -1   118 	if (!state.name) return;
   -1   119 
   -1   120 	var [cmd, sender, arg] = JSON.parse(event.data);
   -1   121 
   -1   122 	if (!state.host && cmd === 'claimHost') {
   -1   123 		state.host = sender;
   -1   124 		if (state.name === state.host) {
   -1   125 			state.word = randomWord();
   -1   126 			document.querySelector('#word').textContent = state.word;
   -1   127 			html.setView('draw-host');
   -1   128 		} else {
   -1   129 			html.setView('draw');
   -1   130 		}
   -1   131 	} else if (state.name === state.host) {
   -1   132 		if (cmd === 'setLie') {
   -1   133 			state.lies[sender] = arg;
   -1   134 			html.updateDoneList('#lie-done', state.lies);
   -1   135 		} else if (cmd === 'setPick') {
   -1   136 			state.picks[sender] = arg;
   -1   137 			html.updateDoneList('#pick-done', state.picks);
   -1   138 		}
   -1   139 	} else if (sender === state.host) {
   -1   140 		if (cmd === 'setImage') {
   -1   141 			document.querySelector('#lie img').src = arg;
   -1   142 			document.querySelector('#pick img').src = arg;
   -1   143 			html.setView('lie');
   -1   144 		} else if (cmd === 'setOptions') {
   -1   145 			html.updateOptionList('#pick ul', arg);
   -1   146 			html.setView('pick');
   -1   147 		} else if (cmd === 'setScores') {
   -1   148 			state.scores = arg;
   -1   149 			resetRound();
   -1   150 			html.updateScoreTable('#scores table', state.scores);
   -1   151 			html.setView('scores');
   -1   152 		}
   -1   153 	}
   -1   154 };

diff --git a/html.js b/html.js

@@ -0,0 +1,57 @@
   -1     1 export var disableForm = function(form, disabled) {
   -1     2 	form.querySelectorAll('button,input').forEach(el => {
   -1     3 		el.disabled = disabled;
   -1     4 	});
   -1     5 	form.classList.toggle('disabled', disabled);
   -1     6 };
   -1     7 
   -1     8 export var updateDoneList = function(selector, names) {
   -1     9 	var ul = document.querySelector(selector);
   -1    10 	ul.textContent = '';
   -1    11 	for (let name in names) {
   -1    12 		var li = document.createElement('li');
   -1    13 		li.textContent = name;
   -1    14 		ul.append(li);
   -1    15 	}
   -1    16 };
   -1    17 
   -1    18 export var updateOptionList = function(selector, options) {
   -1    19 	var ul = document.querySelector(selector);
   -1    20 	ul.textContent = '';
   -1    21 	for (let word of options) {
   -1    22 		var li = document.createElement('li');
   -1    23 		var label = document.createElement('label');
   -1    24 		var radio = document.createElement('input');
   -1    25 		radio.type = 'radio';
   -1    26 		radio.name = 'pick';
   -1    27 		radio.value = word;
   -1    28 		label.textContent = word;
   -1    29 		label.prepend(radio);
   -1    30 		li.append(label);
   -1    31 		ul.append(li);
   -1    32 	}
   -1    33 };
   -1    34 
   -1    35 export var updateScoreTable = function(selector, scores) {
   -1    36 	// TODO crazy animation
   -1    37 	var sorted = Object.entries(scores).sort((a, b) => a[1] > b[1] ? -1 : a[1] == b[1] ? 0 : 1);
   -1    38 
   -1    39 	var table = document.querySelector(selector);
   -1    40 	table.textContent = '';
   -1    41 	for (let [name, score] of sorted) {
   -1    42 		var tr = document.createElement('tr');
   -1    43 		var th = document.createElement('th');
   -1    44 		th.textContent = name;
   -1    45 		tr.append(th);
   -1    46 		var td = document.createElement('td');
   -1    47 		td.textContent = score;
   -1    48 		tr.append(td);
   -1    49 		table.append(tr);
   -1    50 	}
   -1    51 };
   -1    52 
   -1    53 export var setView = function(id) {
   -1    54 	Array.prototype.forEach.call(document.body.children, el => {
   -1    55 		el.hidden = el.id !== id;
   -1    56 	});
   -1    57 };

diff --git a/index.html b/index.html

@@ -0,0 +1,62 @@
   -1     1 <!DOCTYPE html>
   -1     2 <html>
   -1     3 <head>
   -1     4 	<meta charset="utf-8" />
   -1     5 	<title>drawful</title>
   -1     6 	<meta http-equiv="Content-Security-Policy" content="default-src 'self'; connect-src http://via.ce9e.org; img-src data:">
   -1     7 	<meta name="viewport" content="width=device-width" />
   -1     8 	<link rel="stylesheet" href="style.css">
   -1     9 </head>
   -1    10 <body>
   -1    11 	<form id="name">
   -1    12 		<p>Please enter your name</p>
   -1    13 		<input name="username" required>
   -1    14 		<button>Submit</button>
   -1    15 	</form>
   -1    16 
   -1    17 	<form id="scores" hidden>
   -1    18 		<table></table>
   -1    19 		<button>Go next</button>
   -1    20 	</form>
   -1    21 
   -1    22 	<div id="draw" hidden>
   -1    23 		<p>Waiting for player to finish their drawing.</p>
   -1    24 	</div>
   -1    25 
   -1    26 	<form id="draw-host" hidden>
   -1    27 		<p>Draw the following word: <span id="word"></span></p>
   -1    28 		<canvas></canvas>
   -1    29 		<button>Submit</button>
   -1    30 	</form>
   -1    31 
   -1    32 	<form id="lie" hidden>
   -1    33 		<p>Enter a wrong word</p>
   -1    34 		<img/>
   -1    35 		<input name="word" required>
   -1    36 		<button>Submit</button>
   -1    37 		<p class="only-disabled">Waiting for other players to enter their lies.</p>
   -1    38 	</form>
   -1    39 
   -1    40 	<form id="lie-host" hidden>
   -1    41 		<p>Waiting for players to enter their lies.</p>
   -1    42 		<ul id="lie-done"></ul>
   -1    43 		<button>Continue</button>
   -1    44 	</form>
   -1    45 
   -1    46 	<form id="pick" hidden>
   -1    47 		<p>Which of the following words was the original?</p>
   -1    48 		<img/>
   -1    49 		<ul id="options"></ul>
   -1    50 		<button>Submit</button>
   -1    51 		<p class="only-disabled">Waiting for other players to pick their answers.</p>
   -1    52 	</form>
   -1    53 
   -1    54 	<form id="pick-host" hidden>
   -1    55 		<p>Waiting for players to pick their answers.</p>
   -1    56 		<ul id="pick-done"></ul>
   -1    57 		<button>Continue</button>
   -1    58 	</form>
   -1    59 
   -1    60 	<script type="module" src="drawful.js"></script>
   -1    61 </body>
   -1    62 </html>

diff --git a/style.css b/style.css

@@ -0,0 +1,18 @@
   -1     1 img,
   -1     2 canvas {
   -1     3 	display: block;
   -1     4 	width: 50vmin;
   -1     5 	height: 50vmin;
   -1     6 	border: 1px solid;
   -1     7 
   -1     8 	image-rendering: -moz-crisp-edges;
   -1     9 	image-rendering: -webkit-crisp-edges;
   -1    10 	image-rendering: pixelated;
   -1    11 }
   -1    12 
   -1    13 .only-disabled {
   -1    14 	display: none;
   -1    15 }
   -1    16 form.disabled .only-disabled {
   -1    17 	display: block;
   -1    18 }

diff --git a/utils.js b/utils.js

@@ -0,0 +1,14 @@
   -1     1 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
   -1     2 
   -1     3 export var randomString = function(length) {
   -1     4 	var result = [];
   -1     5 	for (var i = 0; i < length; i++) {
   -1     6 		var k = Math.floor(Math.random() * chars.length);
   -1     7 		result.push(chars[k]);
   -1     8 	}
   -1     9 	return result.join('');
   -1    10 };
   -1    11 
   -1    12 export var shuffle = function(arr) {
   -1    13 	return arr.sort(() => Math.random() - 0.5);
   -1    14 };