kana/src/components/Game/Question.jsx

277 lines
9.8 KiB
JavaScript

import React, { Component } from 'react';
import { kanaDictionary } from '../../data/kanaDictionary';
import { quizSettings } from '../../data/quizSettings';
import { findRomajisAtKanaKey, removeFromArray, arrayContains, shuffle, cartesianProduct } from '../../data/helperFuncs';
import './Question.scss';
class Question extends Component {
state = {
previousQuestion: [],
previousAnswer: '',
currentAnswer: '',
currentQuestion: [],
answerOptions: [],
stageProgress: 0
}
// this.setNewQuestion = this.setNewQuestion.bind(this);
// this.handleAnswer = this.handleAnswer.bind(this);
// this.handleAnswerChange = this.handleAnswerChange.bind(this);
// this.handleSubmit = this.handleSubmit.bind(this);
// }
getRandomKanas(amount, include, exclude) {
let randomizedKanas = this.askableKanaKeys.slice();
if(exclude && exclude.length > 0) {
// we're excluding previous question when deciding a new question
randomizedKanas = removeFromArray(exclude, randomizedKanas);
}
if(include && include.length > 0) {
// we arrive here when we're deciding answer options (included = currentQuestion)
// remove included kana
randomizedKanas = removeFromArray(include, randomizedKanas);
shuffle(randomizedKanas);
// cut the size to make looping quicker
randomizedKanas = randomizedKanas.slice(0,20);
// let's remove kanas that have the same answer as included
let searchFor = findRomajisAtKanaKey(include, kanaDictionary)[0];
randomizedKanas = randomizedKanas.filter(character => {
return searchFor!=findRomajisAtKanaKey(character, kanaDictionary)[0];
});
// now let's remove "duplicate" kanas (if two kanas have same answers)
let tempRandomizedKanas = randomizedKanas.slice();
randomizedKanas = randomizedKanas.filter(r => {
let dupeFound = false;
searchFor = findRomajisAtKanaKey(r, kanaDictionary)[0];
tempRandomizedKanas.shift();
tempRandomizedKanas.forEach(w => {
if(findRomajisAtKanaKey(w, kanaDictionary)[0]==searchFor)
dupeFound = true;
});
return !dupeFound;
});
// alright, let's cut the array and add included to the end
randomizedKanas = randomizedKanas.slice(0, amount-1); // -1 so we have room to add included
randomizedKanas.push(include);
shuffle(randomizedKanas);
}
else {
shuffle(randomizedKanas);
randomizedKanas = randomizedKanas.slice(0, amount);
}
return randomizedKanas;
}
setNewQuestion() {
if(this.props.stage!=4)
this.currentQuestion = this.getRandomKanas(1, false, this.previousQuestion);
else
this.currentQuestion = this.getRandomKanas(3, false, this.previousQuestion);
this.setState({currentQuestion: this.currentQuestion});
this.setAnswerOptions();
this.setAllowedAnswers();
// console.log(this.currentQuestion);
}
setAnswerOptions() {
this.answerOptions = this.getRandomKanas(3, this.currentQuestion[0], false);
this.setState({answerOptions: this.answerOptions});
// console.log(this.answerOptions);
}
setAllowedAnswers() {
// console.log(this.currentQuestion);
this.allowedAnswers = [];
if(this.props.stage==1 || this.props.stage==3)
this.allowedAnswers = findRomajisAtKanaKey(this.currentQuestion, kanaDictionary);
else if(this.props.stage==2)
this.allowedAnswers = this.currentQuestion;
else if(this.props.stage==4) {
let tempAllowedAnswers = [];
this.currentQuestion.forEach(key => {
tempAllowedAnswers.push(findRomajisAtKanaKey(key, kanaDictionary));
});
cartesianProduct(tempAllowedAnswers).forEach(answer => {
this.allowedAnswers.push(answer.join(''));
});
}
// console.log(this.allowedAnswers);
}
handleAnswer = answer => {
if(this.props.stage<=2) document.activeElement.blur(); // reset answer button's :active
this.previousQuestion = this.currentQuestion;
this.setState({previousQuestion: this.previousQuestion});
this.previousAnswer = answer;
this.setState({previousAnswer: this.previousAnswer});
this.previousAllowedAnswers = this.allowedAnswers;
if(this.isInAllowedAnswers(this.previousAnswer))
this.stageProgress = this.stageProgress+1;
else
this.stageProgress = this.stageProgress > 0 ? this.stageProgress - 1 : 0;
this.setState({stageProgress: this.stageProgress});
if(this.stageProgress >= quizSettings.stageLength[this.props.stage] && !this.props.isLocked) {
setTimeout(() => { this.props.handleStageUp() }, 300);
}
else
this.setNewQuestion();
}
initializeCharacters() {
this.askableKanas = {};
this.askableKanaKeys = [];
this.askableRomajis = [];
this.previousQuestion = '';
this.previousAnswer = '';
this.stageProgress = 0;
Object.keys(kanaDictionary).forEach(whichKana => {
// console.log(whichKana); // 'hiragana' or 'katakana'
Object.keys(kanaDictionary[whichKana]).forEach(groupName => {
// console.log(groupName); // 'h_group1', ...
// do we want to include this group?
if(arrayContains(groupName, this.props.decidedGroups)) {
// let's merge the group to our askableKanas
this.askableKanas = Object.assign(this.askableKanas, kanaDictionary[whichKana][groupName]['characters']);
Object.keys(kanaDictionary[whichKana][groupName]['characters']).forEach(key => {
// let's add all askable kana keys to array
this.askableKanaKeys.push(key);
this.askableRomajis.push(kanaDictionary[whichKana][groupName]['characters'][key][0]);
});
}
});
});
// console.log(this.askableKanas);
}
getAnswerType() {
if(this.props.stage==2) return 'kana';
else return 'romaji';
}
getShowableQuestion() {
if(this.getAnswerType()=='kana')
return findRomajisAtKanaKey(this.state.currentQuestion, kanaDictionary)[0];
else return this.state.currentQuestion;
}
getPreviousResult() {
let resultString='';
// console.log(this.previousAnswer);
if(this.previousQuestion=='')
resultString = <div className="previous-result none">Let's go! Which character is this?</div>
else {
let rightAnswer = (
this.props.stage==2 ?
findRomajisAtKanaKey(this.previousQuestion, kanaDictionary)[0]
: this.previousQuestion.join('')
)+' = '+ this.previousAllowedAnswers[0];
if(this.isInAllowedAnswers(this.previousAnswer))
resultString = (
<div className="previous-result correct" title="Correct answer!">
<span className="pull-left glyphicon glyphicon-none"></span>{rightAnswer}<span className="pull-right glyphicon glyphicon-ok"></span>
</div>
);
else
resultString = (
<div className="previous-result wrong" title="Wrong answer!">
<span className="pull-left glyphicon glyphicon-none"></span>{rightAnswer}<span className="pull-right glyphicon glyphicon-remove"></span>
</div>
);
}
return resultString;
}
isInAllowedAnswers(previousAnswer) {
// console.log(previousAnswer);
// console.log(this.allowedAnswers);
if(arrayContains(previousAnswer, this.previousAllowedAnswers))
return true;
else return false;
}
handleAnswerChange = e => {
this.setState({currentAnswer: e.target.value.replace(/\s+/g, '')});
}
handleSubmit = e => {
e.preventDefault();
if(this.state.currentAnswer!='') {
this.handleAnswer(this.state.currentAnswer.toLowerCase());
this.setState({currentAnswer: ''});
}
}
componentWillMount() {
this.initializeCharacters();
}
componentDidMount() {
this.setNewQuestion();
}
render() {
let btnClass = "btn btn-default answer-button";
if ('ontouchstart' in window)
btnClass += " no-hover"; // disables hover effect on touch screens
let stageProgressPercentage = Math.round((this.state.stageProgress/quizSettings.stageLength[this.props.stage])*100)+'%';
let stageProgressPercentageStyle = { width: stageProgressPercentage }
return (
<div className="text-center question col-xs-12">
{this.getPreviousResult()}
<div className="big-character">{this.getShowableQuestion()}</div>
<div className="answer-container">
{this.props.stage<3?this.state.answerOptions.map(function(answer, idx) {
return <AnswerButton answer={answer}
className={btnClass}
key={idx}
answertype={this.getAnswerType()}
handleAnswer={this.handleAnswer} />
}, this):
<div className="answer-form-container">
<form onSubmit={this.handleSubmit}>
<input autoFocus className="answer-input" type="text" value={this.state.currentAnswer} onChange={this.handleAnswerChange} />
</form>
</div>
}
</div>
<div className="progress">
<div className="progress-bar progress-bar-info"
role="progressbar"
aria-valuenow={this.state.stageProgress}
aria-valuemin="0"
aria-valuemax={quizSettings.stageLength[this.props.stage]}
style={stageProgressPercentageStyle}
>
<span>Stage {this.props.stage} {this.props.isLocked?' (Locked)':''}</span>
</div>
</div>
</div>
);
}
}
class AnswerButton extends Component {
getShowableAnswer() {
if(this.props.answertype=='romaji')
return findRomajisAtKanaKey(this.props.answer, kanaDictionary)[0];
else return this.props.answer;
}
render() {
return (
<button className={this.props.className} onClick={()=>this.props.handleAnswer(this.getShowableAnswer())}>{this.getShowableAnswer()}</button>
);
}
}
export default Question;