From 123507982e9643a1cf2d747fbd556fc3856bfa49 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Thu, 11 Jun 2020 07:25:15 -0700 Subject: [PATCH] Add support for judging of equivalent answers This involves receiving a list of unique words that were submitted and then allowing the user to group them into words that should be scored as if identical. The interface implemented here uses sequential clicking, but maybe it would be more intuitive to do drag-and-drop instead? I may have to experiement with that. --- empathy/empathy.css | 22 ++++++ empathy/empathy.jsx | 169 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 190 insertions(+), 1 deletion(-) diff --git a/empathy/empathy.css b/empathy/empathy.css index ef88072..7e9bf30 100644 --- a/empathy/empathy.css +++ b/empathy/empathy.css @@ -29,3 +29,25 @@ color: var(--text-fg-on-accent); } } + +.ambiguity-group { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + background-color: var(--accent-color); + margin-bottom: 0.25em; + padding: 0; + border-radius: 10px; +} + +.ambiguity-button { + width: 100%; + border-radius: 10px; + margin: 0; +} + +.ambiguity-button.selected { + background-color: var(--accent-color-bright); + color: var(--text-fg-on-accent-bright); +} diff --git a/empathy/empathy.jsx b/empathy/empathy.jsx index 6f051f4..60540c0 100644 --- a/empathy/empathy.jsx +++ b/empathy/empathy.jsx @@ -60,6 +60,8 @@ events.addEventListener("game-state", event => { window.game.set_active_prompt(state.active_prompt); window.game.set_scores(state.scores); + + window.game.set_ambiguities(state.ambiguities); }); events.addEventListener("prompt", event => { @@ -80,6 +82,18 @@ events.addEventListener("answered", event => { window.game.set_players_answered(players_answered); }); +events.addEventListener("ambiguities", event => { + const ambiguities = JSON.parse(event.data); + + window.game.set_ambiguities(ambiguities); +}); + +events.addEventListener("judged", event => { + const players_judged = JSON.parse(event.data); + + window.game.set_players_judged(players_judged); +}); + events.addEventListener("scores", event => { const scores = JSON.parse(event.data); @@ -305,6 +319,136 @@ const LetsPlay = React.memo(props => { ); }); +class Ambiguities extends React.PureComponent { + + constructor(props) { + super(props); + + this.state = { + word_groups: props.words.map(word => [word]), + submitted: false, + selected: null + }; + } + + async handle_submit() { + const response = await fetch_post_json(`judging/${this.props.prompt.id}`, + this.state.word_groups); + + if (response.status === 200) { + const result = await response.json(); + if (! result.valid) { + add_message("danger", result.message); + return; + } + } else { + add_message("danger", "An error occurred submitting your answers"); + return; + } + + this.setState({ + submitted: true + }); + } + + handle_click(word) { + if (this.state.selected == word) { + /* Second click on same word removes the word from the group. */ + const new_groups = this.state.word_groups.filter( + group => (! group.includes(this.state.selected)) || (group.length > 1)).map( + group => { + return group.filter(w => w !== this.state.selected); + } + ); + this.setState({ + selected: null, + word_groups: [...new_groups, [word]] + }); + } else if (this.state.selected) { + /* Click of a second word groups the two together. */ + const new_groups = this.state.word_groups.filter( + group => (! group.includes(word)) || (group.length > 1)).map( + group => { + if (group.includes(this.state.selected)) { + if (! group.includes(word)) + return [...group, word]; + else + return group; + } else { + return group.filter(w => w !== word); + } + } + ); + this.setState({ + selected: null, + word_groups: new_groups + }); + } else { + /* First click of a word selects it. */ + this.setState({ + selected: word + }); + } + } + + render() { + if (this.state.submitted) + return ( +
+

{this.props.players_judged}/ + {this.props.players_total} players have responded

+

+ Please wait for the rest of the players to complete judging. +

+
+ ); + + const btn_class = "ambiguity-button"; + const btn_selected_class = btn_class + " selected"; + + return ( +
+

Judging Answers

+

+ Click on each pair of answers that should be scored as equivalent, + (and click any word twice to split it out from a group). Remember, + what goes around comes around, so it's best to be generous when + judging. +

+ {this.state.word_groups.map(word_group => { + return ( +
+ {word_group.map(word => { + return ( + + ); + })} +
+ ); + })} +

+ Click here when done judging:
+ +

+
+ ); + } +} + class ActivePrompt extends React.PureComponent { constructor(props) { @@ -407,7 +551,9 @@ class Game extends React.PureComponent { other_players: [], prompts: [], active_prompt: null, - players_answered: 0 + players_answered: 0, + ambiguities: null, + players_judged: 0 }; } @@ -467,6 +613,18 @@ class Game extends React.PureComponent { }); } + set_ambiguities(ambiguities) { + this.setState({ + ambiguities: ambiguities + }); + } + + set_players_judged(players_judged) { + this.setState({ + players_judged: players_judged + }); + } + set_scores(scores) { this.setState({ scores: scores @@ -515,6 +673,15 @@ class Game extends React.PureComponent { ); } + if (state.ambiguities){ + return ; + } + if (state.active_prompt) { return