19 months agoAdd some autofocus attributes to several forms master
Carl Worth [Sat, 19 Sep 2020 21:41:45 +0000 (14:41 -0700)]
Add some autofocus attributes to several forms

Just trying to make things a little more friendly, (so that, a page
with a single form that has a single text field loads with that field
having focus already).

19 months agoCorrect a typo in a comment
Carl Worth [Sat, 19 Sep 2020 20:54:05 +0000 (13:54 -0700)]
Correct a typo in a comment

Obviously, no functional change to the code here.

19 months agoREADME: Add a dependency section
Carl Worth [Sat, 19 Sep 2020 20:56:36 +0000 (13:56 -0700)]
README: Add a dependency section

Mentioning how to install nodejs

19 months agoAdd a README file for the lmno-server repository
Carl Worth [Sat, 19 Sep 2020 20:52:39 +0000 (13:52 -0700)]
Add a README file for the lmno-server repository

It's been a while since I did any LMNO development, and since the last
time I did, I'm now using a new laptop. So I needed a reminder on how
to configure my web server for local development. Here that is now for
my own use, (and for the use of anyone else that might want it).

22 months agoPre-allocate an Empires game with ID QRST
Carl Worth [Tue, 7 Jul 2020 12:58:28 +0000 (05:58 -0700)]
Pre-allocate an Empires game with ID QRST

To simplify the development of the current flempires client.

22 months agolmno: Add a new method to create a game with a specific ID value
Carl Worth [Tue, 7 Jul 2020 12:57:32 +0000 (05:57 -0700)]
lmno: Add a new method to create a game with a specific ID value

This create_game_with_id method will be helpful in pre-allocating
a game for convenien in the development of flempires.

22 months agotest: Extend Scribe testing to encompass a complete game
Carl Worth [Tue, 7 Jul 2020 12:40:03 +0000 (05:40 -0700)]
test: Extend Scribe testing to encompass a complete game

Including the formation of several large-ish glyph shapes.

There's not actually a _lot_ of functional testing here. It would be
much more useful if the server reported whether a move generated a
glyph or not, (and then the test suite could verify that result on
every single move).

For that, we'll also want to aloow sub-tests that don't generate an
entire line of output, but instead just a single letter.

22 months agoDrop an errant blank line
Carl Worth [Tue, 7 Jul 2020 12:37:53 +0000 (05:37 -0700)]
Drop an errant blank line

Just a little cleanup here. Nothing important.

22 months agoImplement glyph detection on the server side
Carl Worth [Tue, 7 Jul 2020 12:36:12 +0000 (05:36 -0700)]
Implement glyph detection on the server side

This seems to work, but it's only been verified via debugging prints,
(that is, the server isn't yet _doing_ anything with the glyph
detection that a client can actually see).

22 months agotest: Add testing for a move that sends you to a full mini-grid
Carl Worth [Mon, 6 Jul 2020 05:35:59 +0000 (22:35 -0700)]
test: Add testing for a move that sends you to a full mini-grid

In this case, it's legal to move anywhere, (the normal constraint of
having to move to a matching mini-square is waived since it's
impossible by definition).

22 months agotest: Add basic testing for Scribe
Carl Worth [Mon, 6 Jul 2020 05:17:18 +0000 (22:17 -0700)]
test: Add basic testing for Scribe

This verifies that the server rejects a move to the wrong mini grid.

22 months agoscribe: Reject moves that are illegal by the move constraint
Carl Worth [Mon, 6 Jul 2020 05:16:11 +0000 (22:16 -0700)]
scribe: Reject moves that are illegal by the move constraint

If the player has a previous move, (this isn't their first move of the
game), then the mini-grid being moved to must match the position
within the mini-grid of the previous move, (unless the indicated mini
grid is full).

22 months agoStop ignoring the .gitattributes file
Carl Worth [Sun, 5 Jul 2020 21:21:31 +0000 (14:21 -0700)]
Stop ignoring the .gitattributes file

Both lmno-todo and nogit have now been reworked to not use
.giattributes, (instead they now use .nogit/info/attributes), so we
don't need to ignore this file anymore.

22 months agoUpdate .gitignore to start using nogit to track our TODO file
Carl Worth [Sun, 5 Jul 2020 07:01:34 +0000 (00:01 -0700)]
Update .gitignore to start using nogit to track our TODO file

Hopefully this will work out as smoothly as I hope it will.

22 months agoInclude inactive players (if they have any points) when sending to clients
Carl Worth [Tue, 30 Jun 2020 00:02:39 +0000 (17:02 -0700)]
Include inactive players (if they have any points) when sending to clients

This allows clients to display scores for all players in a game, (even if
some players happen to not be active anymore).

To make the state consistent, we also send the active bit in the JSON
info for any player.

This commit fixes up the Tic-tac-toe tests in the test suite to now
expect this active property in the info sent from the server.

22 months agoFix bug when a player reclaims a previous player object by name
Carl Worth [Tue, 30 Jun 2020 00:01:29 +0000 (17:01 -0700)]
Fix bug when a player reclaims a previous player object by name

Previously, we were forgetting to update the session ID map, leading
to an experience where the player couldn't submit any words,
etc. (Sorry, David!).

22 months agoempathy: Don't let any player give kudos to themself
Carl Worth [Mon, 29 Jun 2020 21:23:28 +0000 (14:23 -0700)]
empathy: Don't let any player give kudos to themself

Which would not be very sporting of course, so we don't allow it.

Note that this restriction is applied after the majority-ruled
matching. So a player can't even give kudos to a word they didn't
submit and then hope to get matched into it.

It's also worth noting that the group can decide to split a group
where one player gave kudos. That will mean that both sides of the
split get kudos from the player, which seems to match the kudos-giving
player's intent.

22 months agoAdd scoring of kudos along with word groups
Carl Worth [Mon, 29 Jun 2020 20:53:20 +0000 (13:53 -0700)]
Add scoring of kudos along with word groups

The kudos act as a tiebreaker when regular points are otherwise

The test suite is updated here to pass the new information expected by
the server when receiving a word list during judging, (now wants an
object with two properties: 1. words, a list of words, 2. kudos, a

With this the test suite is still fully passing.

22 months agoempathy: Don't allow a prompt with 0 items
Carl Worth [Mon, 29 Jun 2020 20:52:10 +0000 (13:52 -0700)]
empathy: Don't allow a prompt with 0 items

Thanks to my clever nephews for finding this degenerate case that I
had missed when coding this up originally.

22 months agoCut PHASE_IDLE_TIMEOUT in half
Carl Worth [Mon, 29 Jun 2020 15:11:32 +0000 (08:11 -0700)]

The feedback from our playtest session last night was that the one
time we wanted to move on without an idle player, we had to wait too
long before the "Move On" button appeared.

22 months agoRemove a positive vote for a prompt when adding a negative vote
Carl Worth [Sun, 28 Jun 2020 22:04:00 +0000 (15:04 -0700)]
Remove a positive vote for a prompt when adding a negative vote

This is important to do here because the current client interface
doesn't let a player see a prompt again after adding a negative vote,
(so they wouldn't know their positive vote was still there nor could
they remove it themself).

22 months agoChange prompt-retirement to require more negative than votes to retire
Carl Worth [Sun, 28 Jun 2020 21:54:26 +0000 (14:54 -0700)]
Change prompt-retirement to require more negative than votes to retire

Or, as expressed here, more (or equal) positive votes than negative in
order to preserve a prompt.

However the logic is worded, the point is that the old rule "Retire
any prompt with no votes" had a serious flaw: When someone submitted a
new category _just_ before a round started, nobody would get a chance
to vote for it, and then it would be dropped before anyone would see
it on the next round.

Now, instead, just like most everything else in empathy, the retiring
of an undesired category is now determined by a vote.

22 months agoAdd support for negative votes for categories
Carl Worth [Sun, 28 Jun 2020 21:53:43 +0000 (14:53 -0700)]
Add support for negative votes for categories

Just like the positive votes, the endpoint allows toggling and the
storage maintains that each player can only vote for (or against)

22 months agoUse set difference (ANSWERED - JUDGED) to determine whether to advance
Carl Worth [Sun, 28 Jun 2020 00:52:06 +0000 (17:52 -0700)]
Use set difference (ANSWERED - JUDGED) to determine whether to advance

This is a bug fix for the bug described and tested in the previous
commit. Previously we were simply comparing the count of players who
had judged with the count of those who had answered.

Here, instead, we compute an actual set difference and only
auto-advance from the judging phase when every player who answered has
also completed judging.

This fix causes the test added in the previous comit to start passing,
so now the entire test suite is passing once again.

22 months agoAdd new failing test case: Non-player can terminate judging
Carl Worth [Sun, 28 Jun 2020 00:49:00 +0000 (17:49 -0700)]
Add new failing test case: Non-player can terminate judging

The code that is intending to check whether all answering players have
completed judging is currently broken. It is only checking whether the
number of players that have judged is as large as the number of
players that have answered.

This is the wrong logic in the case where a player who didn't answer
joins the game late and submits judging results, which is exactly what
we do in the new test case here.

This test case currently fails, so a fix of the bug described above
will cause it to start passing.

22 months agoTwo alterations to player scoring: per-round grouping, and round normalization
Carl Worth [Sun, 28 Jun 2020 00:21:38 +0000 (17:21 -0700)]
Two alterations to player scoring: per-round grouping, and round normalization

Previously, we were returning an array of player scores where some
succesive players in the array may have had identical scores. Now,
instead, we have only one entry per score, and instead of just a
single player name, an array of player names in case there is a tie at
any score. This should help clients display per-round scores in a way
that make all the ties obvious.

Second, we were previously accumulating the per-round points directly
into a player's total. This had the defect of weighting some rounds
more than others, (rounds with more items were worth a higher maximum
number of points than rounds with fewer items). Now, instead, we only
accumulate into a player's total the number of players that they beat
out in each round. This gives an equal weight to every round.

The test suite is updated in this commit for the first fix above. But
the test suite does not yet cover the player's overall scores so that
change is not tested here.

22 months agoempathy: Add a /new-game endpoint for a majority-rules decision to start again
Carl Worth [Sat, 27 Jun 2020 23:30:23 +0000 (16:30 -0700)]
empathy: Add a /new-game endpoint for a majority-rules decision to start again

This should be a lot friendlier than a single player clicking "new
game" and other players saying, "wait! I was still looking at that".

22 months agoFix JSON stringification of game-state at game reset
Carl Worth [Sat, 27 Jun 2020 21:55:46 +0000 (14:55 -0700)]
Fix JSON stringification of game-state at game reset

The initial stream of game state upon player connection was already
careful to use a subsitute function for correct stringification of
Set objects, but the streaming at game reset was not.

I didn't notice this for a while because the client always reset its
own state data before applying what was streamed from the server. The
application of the server-provided state would fail, (encountering an
empty object instead of an empty array), but since the client state
was already reset anyway, that wasn't noticed, (as long as the failure
only happened after the prompt data, for example).

I uncovered the bug with a recent client fix to not display the
category-input form until the game-state was received, (and the
Boolean to trigger that display happened only after the portions of
the game state that were failing due to the bug described above).

22 months agoAllow a player to reclaim a disconnected player of the same name
Carl Worth [Sat, 27 Jun 2020 17:36:39 +0000 (10:36 -0700)]
Allow a player to reclaim a disconnected player of the same name

I've seen cases where players close their browser window, and then
re-open it with the URL only to find that they aren't actually in the
same session so they end up with an unwanted "Name01" name instead of
"Name" and lose access to their score.

We make things work the way users want here by letting them claim an
existing player object by simply using the same name.

Of course, this does assume nobody will want to do this
nefariously. I'm perfectly fine with that, (since the whole point of
all of this is to enable friendly games). If, at some point in the
future somebody cared to lock people out from doign this, then we can
imagine a future where:

1. Users could actually log in

2. Users could lock a game to only allow logged-in users

22 months agoDon't send a re-connecting player a player-enter event for themself
Carl Worth [Sat, 27 Jun 2020 17:26:29 +0000 (10:26 -0700)]
Don't send a re-connecting player a player-enter event for themself

It's rather ugly that we've got these semantics where the client
doesn't want to see a player-enter event for itself. This is causing
too many special cases in the code (such as the current commit).

So we'll want to change that at some point. But for now we make the
case of a player reconnecting match the semantics. Specifically we
move the add_connection call down _after_ the broadcast of the
player-enter event. This fixes the current client so that it doesn't
display its own name twice when reclaiming a pre-existing player.

22 months agoFix player-enter events to use complete info_json() method
Carl Worth [Sat, 27 Jun 2020 17:20:25 +0000 (10:20 -0700)]
Fix player-enter events to use complete info_json() method

Previous to this commit, the player-enter event was hardcoded to send
only ID and name properties, which was enough for an actual new
player. But a player-enter event can also happen when a player
re-joins after being disconnected. In this case, we want the score to
be sent as well.

We can get everything we want by using the player's info_json() method
rather than doing a custom call to JSON.stringify with a hard-coded
list of properties.

22 months agogame: Fix reactivation of an existing player to be fully active
Carl Worth [Sat, 27 Jun 2020 16:45:22 +0000 (09:45 -0700)]
game: Fix reactivation of an existing player to be fully active

To do this, we have to set the player's active bit to true, then we
increment the server's count of active players, and finally we
broadcast to all active players that this player has entered the game

This commit fixes the bug described in the previous commit, (so that
the entire test suite passes once again).

22 months agoAdd new failing test case for a reactivated player
Carl Worth [Sat, 27 Jun 2020 16:35:59 +0000 (09:35 -0700)]
Add new failing test case for a reactivated player

I noticed that when a player is reactivated, (that is, by starting a
new event stream by means of a cookie associated with a pre-existing
cookie for a player that had been deactivated), the server was not
properly activating the player.

This commit adds a new failing test case that demonstrates the bug. We
reactivate dale for a 4-player game, but then the server advances the
game out of the answering phase when only 3 players have submitted an
answer. This demonstrates that dale isn't being considered active.

22 months agoempathy: Don't count inactive players to advance from answering phase
Carl Worth [Sat, 27 Jun 2020 16:25:20 +0000 (09:25 -0700)]
empathy: Don't count inactive players to advance from answering phase

This commit implements the desired behavior described in the previous
commit message. The test case that was added in that commit now
passes, (as does the entire test suite).

In the implementation here, we need a count of all player that have
the active property set to true. Instead of looping over the array and
counting this, we have the Game class track this count as players are
added and removed.

22 months agoAdd new failing test case: Inactive players shouldn't impede phase advance
Carl Worth [Sat, 27 Jun 2020 16:11:42 +0000 (09:11 -0700)]
Add new failing test case: Inactive players shouldn't impede phase advance

The current implementation is waiting for all registered players to
have submitted answers before auto-advance (prior to a majority vote
to move on without anyone).

That seems like reasonable logic, except for a player that the server
has already noticed is no longer active (event stream connection has
dropped and server has indicated the player is gone from the game). We
don't want such an inactive player impeding the progress of the game.

This commit adds a test case capturing the behavior we actually want,
(so this test is currently failing).

22 months agoAdd clean handling of Control-C to the test suite
Carl Worth [Sat, 27 Jun 2020 15:59:25 +0000 (08:59 -0700)]
Add clean handling of Control-C to the test suite

Interrupting the test suite will noe run all cleanup necessary, (such
as terminating the child processes that are still actively listening
to empathy event streams), and will give a (partial) test report of
all tests that have been completed.

This is convenient for when a user doesn't want to wait for the slow
tests to complete, but can still get some good information from
sumarizing the results of all of the fast tests that have already been

22 months agotest: Maintain a list of activated players for automated cleanup
Carl Worth [Sat, 27 Jun 2020 15:43:28 +0000 (08:43 -0700)]
test: Maintain a list of activated players for automated cleanup

The list of deactivate calls at the end of the test sutie was getting
rather messy, (since it required awareness of which players had been
activated in specific tests and then also which had _not_ been
deactivated in other tests).

Instead, we now store an array of each activated player, and loop over
that deactivating all players at the end. We also make deactivate
idempotent, safely doing nothing if we ask to deactivate a plyer which
was already deactivated.

22 months agoOmit inactive players when reporting scores
Carl Worth [Sat, 27 Jun 2020 15:29:49 +0000 (08:29 -0700)]
Omit inactive players when reporting scores

If they have left the game, then we don't need to report a score for
them in this round, since they obviously didn't play in it.

With this commit, the new test added in the previous commit now
passes, and the entire test suite is back to passing again.

22 months agoAdd new failing test case demonstrating zombies sticking around
Carl Worth [Sat, 27 Jun 2020 15:27:19 +0000 (08:27 -0700)]
Add new failing test case demonstrating zombies sticking around

If a player has entirely dropped from the game, then we shouldn't be
reporting a score for them as if they are still active.

This commit adds a new (slow!) test which currently fails to expose
this undesired behavior.

22 months agoAdd a new empathy_player_deactivate function
Carl Worth [Sat, 27 Jun 2020 15:18:26 +0000 (08:18 -0700)]
Add a new empathy_player_deactivate function

To pair with empathy_player_activate and abstract away the goofiness
of pkill and the player_pid=${player}_pid; ${!player_pid} magic.

22 months agoOnly count players who participated in judging when determining quorum
Carl Worth [Sat, 27 Jun 2020 15:00:20 +0000 (08:00 -0700)]
Only count players who participated in judging when determining quorum

Players who are inactive, (whether they registered and then never
submitted answers, or submitted answers but then didn't submit any
judging results), should not be able to sway the results of the

This commit fixes the failing test case of the previous commit so that
the entire test suite is passing once again.

22 months agoAdd failing test case for a major match-voting bug
Carl Worth [Sat, 27 Jun 2020 14:55:09 +0000 (07:55 -0700)]
Add failing test case for a major match-voting bug

Specifically, the majority requirement for a word match to be
considered succesful has been counting all registered players rather
than only the players actually in the current game.

In the degenerate case, having more inactive registered players than
active would mean that no matches would ever be judged as valid even
with unanimous voting. Obviously, this is a disastrous bug.

The test case added here currently fails, (where one person voting
should be considered a (non-strict) majority in a 2-person game).

22 months agoAdd all players who have answered as "judging" when judging phase starts
Carl Worth [Sat, 27 Jun 2020 14:44:31 +0000 (07:44 -0700)]
Add all players who have answered as "judging" when judging phase starts

The rationale for this change is explained more fully in the commit
message of the previous commit.

This change fixes the failing test case added in the previous commit
so the test suite now entirely passes again.

22 months agoAdd failing test for list of judging players when judging phase begins
Carl Worth [Sat, 27 Jun 2020 14:39:53 +0000 (07:39 -0700)]
Add failing test for list of judging players when judging phase begins

In the answering phase, the list of answering players (the ones we are
waiting for) initially starts out as empty and then gets populated
only as people start typing (that is, submit a post to the
"/answering" endpoint).

That's appropriate since we don't know who among the players
registered are really actively "playing".

But that's not the right logic for the judging phase. The judging
phase starts immediately after the answering phase has completed, so
at that point we know the precise list of active players and we should
display all of them in the "waiting" list until each has submitted.

This commit adds a failing test case demonstrating that this list is
empty when the judging phase starts, but it should include the names
of each player that submitted an answer, (but not the name of a player
that is registered but did not submit).

22 months agoFix bug described in the previous commit
Carl Worth [Sat, 27 Jun 2020 14:31:52 +0000 (07:31 -0700)]
Fix bug described in the previous commit

With this fix, we now advance to the scoring phase as soon as the same
number of people have completed judging as submitted answers in the
first place. (That is, don't wait for a registered player that didn't
participate in the answering phase to now participate in the judging

With this fix, the failing test added in the previous commit now
passes, so the test suite is now fully passing.

22 months agotest: Add failing test case for unnecessary delay for end-judging voting
Carl Worth [Sat, 27 Jun 2020 14:27:41 +0000 (07:27 -0700)]
test: Add failing test case for unnecessary delay for end-judging voting

If all of the players that submitted answers have also all completed
judging, then there's nobody left to wait for and it's pointless to
wait for the idle timer to fire and to force everyone to vote to end
judging. Yet, that's what the implementation currently does.

This commit adds a new test case, which fails, showing the point at
which the game should automatically advance to scoring, (once every
player who submitted an answer has also voted).

22 months agoWrap curl_get_cookie and curl_post_cookie in empathy_get/empathy_post
Carl Worth [Sat, 27 Jun 2020 14:16:40 +0000 (07:16 -0700)]
Wrap curl_get_cookie and curl_post_cookie in empathy_get/empathy_post

This allows for simpler calls tat don't need to each keep repeating te
empathy_game_path variable over and over.

While here we add usage documentation to each of the empathy utility
functions as well.

22 months agotest: Keep player event streams open
Carl Worth [Sat, 27 Jun 2020 13:50:00 +0000 (06:50 -0700)]
test: Keep player event streams open

We're about to fix the server to properly notice when a player drops
their connection and then consider them inactive. To prepare for this
in the test suite, here we put the curl calls getting the event stream
into the background so they stick around, and finally kill them at the
end of the test suite.

We use an ugly `eval` based approach so that emapthy_player_activate
with a name like "alice" sets both an $alice_cookie and an $alice_pid
variable. (Sure would be nice if we had real data structures. Oh

22 months agoStart the judging_idle timer whenever a judgment is received
Carl Worth [Fri, 26 Jun 2020 14:45:10 +0000 (07:45 -0700)]
Start the judging_idle timer whenever a judgment is received

Previously, we were starting the judging_idle timer only when a user
reported they were in the process of judging. This caused a bug in the
following scenario:

  * No players group any words

  * Majority of players submit the word groups unchanged

  * Minority of players drop from the game


At this point, the game is supposed to consider the judging phase as
idle, but it wasn't doing this because the idle timer never got
started, (because no players actually grouped any words).

This bug came up in some (admittedly artificial) manual
testing. There's no chane to the test suite here because the current
test suite isn't yet exercising the timing related aspects of the
game, (the phase idle timeouts, etc.).

22 months agoFix some typos in some code comments
Carl Worth [Fri, 26 Jun 2020 14:42:37 +0000 (07:42 -0700)]
Fix some typos in some code comments

Mostly some confusion of "this", "the", and "that".

22 months agoRefactor admin page a bit
Carl Worth [Thu, 25 Jun 2020 03:19:18 +0000 (20:19 -0700)]
Refactor admin page a bit

Using a new filter to map an array to a new array of just a single
property value from each object of the original array.

This lets us avoid using the "for" directive in the template, as well
as use the "join" filter to get commas between each item in the list,
(which is hard to do with the "for" directive in the template).

22 months agoadmin: Fix admin page to correctly show active/idle games and players
Carl Worth [Thu, 25 Jun 2020 01:32:54 +0000 (18:32 -0700)]
admin: Fix admin page to correctly show active/idle games and players

A while ago we changed the storage for players. Previously we had a
_players array and a separate clients array for their
connections. Then at some point in the past we changed to an array
named "players" and instead of separate "clients" each player in
"players" now has its own list of connections.

Ever since that change the admin view has been broken since it wasn't
updated to track that change. Here we bring it up to date, (including
the addition of two nunjucks filters, "active" and "idle" to help with

22 months agoMark players as active:false when they drop all connections
Carl Worth [Thu, 25 Jun 2020 01:29:45 +0000 (18:29 -0700)]
Mark players as active:false when they drop all connections

Prior to this commit, the intent had been to delete players entirely
when they had no remaining connections. But this code was broken for
the same reason as the bug fixed in the previous commit (filter
returns a new array).

But instead of fixing that bug, here we're actually changing the
semantics so that once a player has so remaining connections they are
simply marked as active:false. This is still broadcast out to all
active players as a "player-exit" event but it means the server is
holding onto the data so that a player can reclaim their spot (and
their score) by rejoining later.

22 months agogame: Fix to actually drop connections that get closed
Carl Worth [Thu, 25 Jun 2020 01:28:34 +0000 (18:28 -0700)]
game: Fix to actually drop connections that get closed

The bug here is that filter() returns a new array, so I have to assign
the return value to the original reference for this code to have any

22 months agoSet judging_start_time_ms before anyone actually does any judging
Carl Worth [Thu, 18 Jun 2020 16:04:29 +0000 (09:04 -0700)]
Set judging_start_time_ms before anyone actually does any judging

The judging phase is different than answering in that judging is
optional. So we can't rely on clients actually sending a "judging"
notification, (since they might just submit without having clicked on
any of the words). So instead, we set the start time for the judging
phase as soon as we enter that phase.

22 months agoAdapt the idle treatment from the answering phase to the judging phase
Carl Worth [Thu, 18 Jun 2020 15:37:03 +0000 (08:37 -0700)]
Adapt the idle treatment from the answering phase to the judging phase

Big copy/paste here, (obviously it would be better to capture the
common code in a "phase" module of some sort).

Note that this is currently untested, only because we haven't gotten
the client listening for judging-idle yet.

22 months agoReset timer handle after clearTimeout
Carl Worth [Thu, 18 Jun 2020 15:15:39 +0000 (08:15 -0700)]
Reset timer handle after clearTimeout

This helps make clear that the timer has been cleared, (so that we
don't accidentally call clearTimeout again in the future or anything
like that).

22 months agoFix bug attempting to set "answering_idle" state
Carl Worth [Thu, 18 Jun 2020 15:11:00 +0000 (08:11 -0700)]
Fix bug attempting to set "answering_idle" state

So annoying to have no static compiler checks for assigning to a
mistyped property like this.

Anyway, the impact of the bug here would have been fairly minimal: If
a user reloaded during idle, they would not have gotten the "Move On"
button like expected.

22 months agoExtend PHASE_IDLE_TIMEOUT from 10 seconds to 30 seconds
Carl Worth [Thu, 18 Jun 2020 14:59:17 +0000 (07:59 -0700)]
Extend PHASE_IDLE_TIMEOUT from 10 seconds to 30 seconds

The 10 second value was always known to be aggressively short. I had
this in there with such a short value because we didn't have the
feature to auto-advance a phase after every single player was in, nor
to auto-idle the phase after the players-waiting list is emptied. So
without those features implemented, players were forced to wait for
the idle timeout in situations where there was nothing to wait for. So
to keep that from being too annoying, I had this parameter dialed

But now that those two missing features were implemented in recent
commits, we take this timeout up to a higher value. With this,
hopefully the Move On button won't appear too soon so that people
inadvertently skip an active player, but instead only appears once
people really are stuck waiting.

22 months agoAdd named parameters for the two idle timer controls
Carl Worth [Thu, 18 Jun 2020 14:57:27 +0000 (07:57 -0700)]
Add named parameters for the two idle timer controls

These controls may need some tweaking, so it's better to have some
defined names for them along with some carefully specified semantics.

Also, we plan to extend the current idle detection from the answering
phased to the judging phase as well, so it will be nice to be able to
reuse these parameters there too.

22 months agoBroadcast "answering-idle" as soon as the waiting list becomes empty
Carl Worth [Thu, 18 Jun 2020 14:49:16 +0000 (07:49 -0700)]
Broadcast "answering-idle" as soon as the waiting list becomes empty

That is, as long as the answering phase has been going on for a "while".

This avoids an awkward pause when all active players are done
answering, everybody wants to move on, but the "Move On" button hasn't
appeared yet, (because clients haven't been told yet that the
answering phase is idle).

22 months agoInclude still-answering players when ruling on end-asnwering majority
Carl Worth [Fri, 26 Jun 2020 14:33:04 +0000 (07:33 -0700)]
Include still-answering players when ruling on end-asnwering majority

The test suite exposed a logical bug here. Imagine a 10-player game
where two players have submitted and 8 are still answering. Those two
players should not have the ability to move on without the answers
from the larger set of players still waiting. But that's what the old
logic would have allowed here.

In this commit we count up all players who have already submitted
answers as well as all players who have at least indicated that they
are answering in order to decide how many we need for a majority.

Note: This does mean that if something happens, (like a major network
outage), that prevents a majority of the players from being able to
submit their answers, the game cannot proceed with the minority who
succesfully submitted. I think I'm OK with that. (If the minority
really wants to proceed in a case as dramatic as that then it would be
reasonable for them to start a new game.)

With this commit the test suite is now fully passing again, (for the
first time since the recent addition of new tests).

22 months agoConsistently use null value to reset the ambiguities list
Carl Worth [Fri, 26 Jun 2020 14:31:43 +0000 (07:31 -0700)]
Consistently use null value to reset the ambiguities list

The constructor has always been setting ambiguities to null, but on
game reset it was getting set to 0 instead. The test suite noticed
this inconsistency, which we fix here to make a recently-added test
start passing.

22 months agoSend a response in the implementation of the '/reset' endpoint
Carl Worth [Fri, 26 Jun 2020 14:30:38 +0000 (07:30 -0700)]
Send a response in the implementation of the '/reset' endpoint

Without this, a client could hang forever waiting for the server to
say something, (which is exactly what the test suite was doing without
this fix).

22 months agoExpand test suite to include testing of auto-phase advancement
Carl Worth [Fri, 26 Jun 2020 14:26:05 +0000 (07:26 -0700)]
Expand test suite to include testing of auto-phase advancement

The previous commit makes it so that when all involved players are
ready to move on, (such as, everyone has submitted their answers), the
game automatically advances to the next phase.

This breaks some existing tests, which had been expecting the game to
wait for everyone to vote to advance (the old, clunky behavior).

In this commit, we fix the test suite to expect the auto advancing in
the first ame (while also adding a check to ensure it doesn't
auto-advance until the last player is ready).

We also add a second game where auto advancing doesn't happen, (one
player is never ready) but a majority of the remaining players vote to
advance without them.

These tests do exhibit a couple of bugs in the current implementation,
(so some failures are expected here). These failures will be fixed in
the immediately following commits.

22 months agoEmpathy: Eliminate delay when there's nobody to wait for
Carl Worth [Thu, 18 Jun 2020 14:31:32 +0000 (07:31 -0700)]
Empathy: Eliminate delay when there's nobody to wait for

We don't even need to see the "Move On" button appear if every single
registered player has completed the answering or judging task.

22 months agoTweak idle timer down from 30 seconds to 10
Carl Worth [Sun, 14 Jun 2020 23:59:33 +0000 (16:59 -0700)]
Tweak idle timer down from 30 seconds to 10

I'm nervos that otherwise the game will impose a long delay after the
final player submits their answers.

22 months agoTrim leading/trailing whitespace when canonizing words
Carl Worth [Sun, 14 Jun 2020 23:48:56 +0000 (16:48 -0700)]
Trim leading/trailing whitespace when canonizing words

We have a theory that this is what was causing otherwise
identical-looking values to appear next to each other in the judging
lists of recent games.

22 months agoImplement an idle event for when nobody has been typing for a while
Carl Worth [Sun, 14 Jun 2020 23:42:33 +0000 (16:42 -0700)]
Implement an idle event for when nobody has been typing for a while

During the answering phase, if no player has been active, (that is, by
submitting an "answering" event), for 30 seconds, then submit an
"answering-idle" event to all players.

22 months agoTo rule on the judging "Move On" button require a maority of answered players
Carl Worth [Sun, 14 Jun 2020 23:40:35 +0000 (16:40 -0700)]
To rule on the judging "Move On" button require a maority of answered players

At this point in the game we know a reliable number of players that
are actually "in" this round, so use that when determining the
majority required.

22 months agoRemove player names from answering and judging when they submit
Carl Worth [Sun, 14 Jun 2020 21:57:10 +0000 (14:57 -0700)]
Remove player names from answering and judging when they submit

Without this fix, reloading the "still waiting" view would make player
names that had previously submitted erroneously appear in the "still
waiting" list once again.

22 months agoFix game-state object to include players judging and answering
Carl Worth [Sun, 14 Jun 2020 21:55:12 +0000 (14:55 -0700)]
Fix game-state object to include players judging and answering

Previously, these were always appearing as empty objects in the JSON
results that we streamed from the server in the game-state object
because JSON.stringify doesn't natively know how to serialize a Set

Here, we provide a replacer function that serializes a Set object as
an array.

22 months agoAdd "/answering" and "/judging" endpoints
Carl Worth [Sun, 14 Jun 2020 18:18:18 +0000 (11:18 -0700)]
Add "/answering" and "/judging" endpoints

These are set up so that clients can report when they are doing any
answering or judging. The server then passes this along so that other
clients can be aware of what's going on.

22 months agoRename "/judging" endpoint to "/judged"
Carl Worth [Sun, 14 Jun 2020 17:18:12 +0000 (10:18 -0700)]
Rename "/judging" endpoint to "/judged"

This is to make room for a new "/judging" endpoint to capture the
notification that a player is in the process of performing judging,
(that they've started clicking on some of the words in the list).

This commit also fixes the test suite to track this change.

Note: "judged" isn't really a perfect name here. By analogy with
"answer" what we want here is a noun, which would imply "judgment" but
that suggests an entirely different meaning to my ears. So, for now,
we're going with the slightly inconsistent "judged".

22 months agoTweak game-ID generation toward more frequently used letters
Carl Worth [Sun, 14 Jun 2020 16:37:27 +0000 (09:37 -0700)]
Tweak game-ID generation toward more frequently used letters

I'd noticed recently that game IDs seemed to have more Qs, Zs, and Js
than it felt like they should. In fact, all letters were being given
an equal probability of appearing, but that doesn't match our
expectation that some letters should be "rare".

In this commit we tweak the selection of letters toward more "common"
letters. This should hopefully give game ID strings that are slightly
more "friendly" looking in general.

The set of available game ID values isn't reduced here (at least
because of the above, but it is reduced because of part of what is
described below), but when many game IDs are allocated it can now take
longer for the random-selection algorithm to find an available one.

We also switch from using 'B' and 'F'/'X' to instead using 'P' and
'S', again favoring letters that English speakers expect to be

This actually fixes a bug which I hadn't noticed before, in that the
code has always been replacing a '5' in the input with 'S' but doing
that after 'S' was replaced with 'F', so any input with '5' would not
be mapped as intended. Now an input of '5' will work in the place of

22 months agoAdapt test suite to add explicit voting to advance phases
Carl Worth [Thu, 25 Jun 2020 16:08:53 +0000 (09:08 -0700)]
Adapt test suite to add explicit voting to advance phases

As of the previous commit, it's now required for players to explicit
vote to advance from one phase to the next. That commit broke the test
suite which didn't have this voting added.

In this commit, we fix the test suite by adding that voting (and
ensuring the state game state transitions from before to after the

23 months agoAccept votes to advance game at /end-answers and /end-judging endpoints
Carl Worth [Sat, 13 Jun 2020 21:38:28 +0000 (14:38 -0700)]
Accept votes to advance game at /end-answers and /end-judging endpoints

The previous logic was far too strict, where the game would advance
only after every registered player had submitted. That made it
possible for the game to become totally stuck with just a single
player that left the game, (or even that accidentally reloaded the
game in a way that lost their previous session).

Now there are endpoints for players to vote that they would like to
advance past either the "answers" or "judging" phases of the game, and
the game will advance as soon as a majority of the registered players
have voted.

This does still make it possible for the game to get stuck if a
sufficient number of players stop playing, but it's at least less
likely to happen. And we've got plans to improve this situation
further soon as well.

23 months agoForce all submitted judging words to lowercase before mapping
Carl Worth [Fri, 12 Jun 2020 17:24:41 +0000 (10:24 -0700)]
Force all submitted judging words to lowercase before mapping

This fixes the bug revealed in the previous commit so that the test
suite now passes once again.

The server now doesn't apy attention to the capitalization that a
client happens to use when providing a judging submission, but will
treat things as equivalent regardless of case differences.

23 months agotest: Stress the implementation a bit with a mixed-case judging report
Carl Worth [Fri, 12 Jun 2020 17:18:00 +0000 (10:18 -0700)]
test: Stress the implementation a bit with a mixed-case judging report

With this change to the test, we now have a user submitting a judging
report with "grains of sand" in a list after having received "Grains
of Sand" from the server.

The server currently misscores in this case, so this test case now
fails, (revealing a bug in the server).

23 months agoFix bug with incorrect scoring based on capitalization
Carl Worth [Fri, 12 Jun 2020 17:10:06 +0000 (10:10 -0700)]
Fix bug with incorrect scoring based on capitalization

As exposed in the previous commit which extended testing.

After we construct what we call "word maps", (where a group of words
are hased to by each word in the group individually), we collapse them
down to just the array of word groups themselves. This step was being
carried out by looking for the mapping to the word group where the
mapped word matched the first word in the list.

However, if that first word happened to differ in case from the
mapping word (which was already made canonically lowercase) then the
word group wouldn't be included in the final array and all submissions
of words from that group would incorrectly be scored as singletons.

The fix for this is to simply force the word being compared to as in
this commit.

This commit causes the test suite to pass again.

23 months agotest: Empathy: Extend test to submit judging results with proper capitalization
Carl Worth [Fri, 12 Jun 2020 16:56:35 +0000 (09:56 -0700)]
test: Empathy: Extend test to submit judging results with proper capitalization

In commit abea9915939bd7f5b6e70f12d724f19dc6694ec9 we extended the
test suite to cover the case of players submitting words with mixed
capitalization and we ensured that the server didn't send out any
duplicate words for judging, (that it ignored case when determining
which words were unique).

However, in that commit we still had the players returning the results
of their judging in all lowercase. This is not a good test since
actual clients will return words exactly as they received them.

Here we improve the test by submitting judging results with the same
capitalization that the server used when sending out the
ambiguities. And we also expect that same capitalization to appear in
the final word list.

However, this test currently fails in the final word list _and_ also
fails in the scoring. So this test is exposing a bug that currently
exists in the server.

23 months agotest: Empathy: Add test for final words submitted
Carl Worth [Fri, 12 Jun 2020 16:53:10 +0000 (09:53 -0700)]
test: Empathy: Add test for final words submitted

Verifying that we get exactly the results that we expect, (including
all groups and all singletons).

This new test currently passes, (hurrah!).

23 months agoempathy: Alphabetize list before sending it out for judging
Carl Worth [Fri, 12 Jun 2020 02:58:33 +0000 (19:58 -0700)]
empathy: Alphabetize list before sending it out for judging

This should make judging much easier.

Fix is confirmed with the existing test case (which is updated to
match the new behavior).

23 months agoEmpathy: Fix the capitalization bug
Carl Worth [Fri, 12 Jun 2020 02:53:15 +0000 (19:53 -0700)]
Empathy: Fix the capitalization bug

The simple fix here is to always use the canonize function before
passing any word into the word_map.

With this fix, the test implemented in the previous commit now passes.

23 months agotest: Expand empathy test case to include mixed capitalization
Carl Worth [Fri, 12 Jun 2020 02:49:12 +0000 (19:49 -0700)]
test: Expand empathy test case to include mixed capitalization

When actually playing a live game recently, we noticed it normalized
different capitalization when sending words out to be judged, but
didn't normalize it when scoring. So we add a test case for this.

It currently fails here, (showing the bug). When the bug is fixed this
test should start passing without needing any change to the test

23 months agotest: Add test case for rejection of category that is far too large
Carl Worth [Fri, 12 Jun 2020 00:07:20 +0000 (17:07 -0700)]
test: Add test case for rejection of category that is far too large

As implemented in the previous commit.

23 months agoEmpathy: Reject categories with more than 20 items
Carl Worth [Fri, 12 Jun 2020 00:06:28 +0000 (17:06 -0700)]
Empathy: Reject categories with more than 20 items

Just to avoid any smart aleck trying to get every player's browser to
try to render a million text input fields.

23 months agotest: Exercise the situation coded for in the previous commit
Carl Worth [Thu, 11 Jun 2020 23:41:31 +0000 (16:41 -0700)]
test: Exercise the situation coded for in the previous commit

Specifically, by changing bob's answer of "surfers" to "sand" he
should now receive one less point than before. That's because
"surfers" was worth one point (since only bob submitted it), but now
"sand" is worth 0. And _that's_ because bob is already getting full
credit of 3 points from "sands" for matching everybody, so he doesn't
deserve any additional credit for putting down an additional answer
that the group decided is equivalent to one he already has.

23 months agoEmpathy: Use word groups to assign scores to players, not their lists
Carl Worth [Thu, 11 Jun 2020 23:37:43 +0000 (16:37 -0700)]
Empathy: Use word groups to assign scores to players, not their lists

Previously, we were marching through each players list of words and
looking each word up in the word groups to find a score for that
word. This worked in general, but failed in a specific case:

If a player had two different words that were considered by the group
to be equivalent, that player received the score for that word
twice. To avoid giving a palyer in this situation points that the
group clearly didn't consider they deserved, we instead iterative over
the worud groups themselves to assign points out to players.

This means that when a player ends up having two (or more) words that
end up being equivalent, (based on the team consensus when judging),
all but one of those words will be worth nothing.

23 months agoEmpathy: Change /judging endpoint to expect a top-level word_groups property
Carl Worth [Thu, 11 Jun 2020 22:57:24 +0000 (15:57 -0700)]
Empathy: Change /judging endpoint to expect a top-level word_groups property

It's probably wise, (for future-proofing our APIs), if the server
looks for an explicitly-named property rather than just slurping up
the whole request body.

This also keeps the code more consistent with other endpoints, (which
is going to be particularly important as I start refactoring things to
remove some duplicated code).

23 months agoEmpathy: test: Including final score in testing
Carl Worth [Thu, 11 Jun 2020 22:52:28 +0000 (15:52 -0700)]
Empathy: test: Including final score in testing

And expand the answers a bit to eliminate duplicate scores between
alice and bob. This test verifies that the game is properly scored
according to the word groups that the players submitted.

23 months agoEmpathy: Initial implementation of scoring that considers players' judging
Carl Worth [Thu, 11 Jun 2020 22:26:17 +0000 (15:26 -0700)]
Empathy: Initial implementation of scoring that considers players' judging

I'm slowly learning more about JavaScript, (such as using
Objects.entries instead of open-coding it, using Set(), etc.), which
is helping as I write progressively more complex code.

But I don't feel _really_ confident that I've written this code in the
best form possible.

23 months agoDon't throw out punctuation when "canonizing" words
Carl Worth [Thu, 11 Jun 2020 22:23:32 +0000 (15:23 -0700)]
Don't throw out punctuation when "canonizing" words

This caused a pretty disastrous failure of the game when the team
chose "Programming languages"; people submitted C, C++, and C#; and
the word list they were presented for voting had only C# in it.

23 months agotest: Add initial testing for the Empathy game
Carl Worth [Thu, 11 Jun 2020 19:11:45 +0000 (12:11 -0700)]
test: Add initial testing for the Empathy game

Two general techniques that are introduced here that will be useful
for testing of other games:

  * Registration of multiple user and events sent from each (see
    multiple cookies and a player-cookie parameter for many empathy_*

  * Fishing events out of the event stream in response to client
    requests (see curl_get_event)

This testing gets as far as is currently implemented in the server:
Multiple player join, a category is submitted and voted one, the game
starts, all players submit answers, players receive words to judge,
and players submit word groups to capture the results of their

What's not yet tested is the next phase that's also not implemented
yet: The server using the submitted word groups to affect the scoring.

23 months agoProvide an explicit (empty) return body for some responses
Carl Worth [Thu, 11 Jun 2020 18:53:43 +0000 (11:53 -0700)]
Provide an explicit (empty) return body for some responses

If we just do the numeric sendStatus(200) then node goes and sends a
response with a body of "OK" rather than something empty.

I don't necessarily want that, (and I notice that when I have spurious
"OK" strings cluttering up my test-suite output).

23 months agoempathy: Fix /prompts endpoint to return the ID of the new prompt
Carl Worth [Thu, 11 Jun 2020 14:52:26 +0000 (07:52 -0700)]
empathy: Fix /prompts endpoint to return the ID of the new prompt

It never ceases to amaze me the bugs that can only be found when
writing tests. (My manual client testing missed this bug because they
never looked for this ID, but instead picked up the ID from the
broadcast to all clients when a new prompt is added.)

23 months agotest: Rename cookie file from .test-cookie to .cookie-tictactoe
Carl Worth [Thu, 11 Jun 2020 14:36:56 +0000 (07:36 -0700)]
test: Rename cookie file from .test-cookie to .cookie-tictactoe

In order to have a little independence between test sections, let's
move to having independent cookie files for each.

23 months agoAdd a game phase to perform judging
Carl Worth [Thu, 11 Jun 2020 14:22:29 +0000 (07:22 -0700)]
Add a game phase to perform judging

This is after all answers are submitted, and before scores are computed.

The list of unique words are sent to each player so that they can
group them into sets of equivalent words.

The server isn't yet _doing_ anything with the word groups that are
submitted from judging, but that will be the next step.

23 months agoEmpathy: Delay the updating of total scores until a new round
Carl Worth [Thu, 11 Jun 2020 02:26:51 +0000 (19:26 -0700)]
Empathy: Delay the updating of total scores until a new round

Instead of doing this in the compute_scores function (for the current
round) as we may end up having that function called multiple times.

23 months agoAccumulate running scores for a multi-round game
Carl Worth [Wed, 10 Jun 2020 16:15:36 +0000 (09:15 -0700)]
Accumulate running scores for a multi-round game

Obviously, if players stick around for more than one round, they are
going to want to see hwo their score accumulates.