diff --git a/js/common/enum/GameState.js b/js/common/enum/GameState.js new file mode 100644 index 00000000..68c6b468 --- /dev/null +++ b/js/common/enum/GameState.js @@ -0,0 +1,17 @@ +// Copyright 2002-2014, University of Colorado Boulder + +/** + * Possible game states in the 'Arithmetic'. + * + * @author Andrey Zelenkov (Mlearner) + */ +define( function() { + 'use strict'; + + return Object.freeze( { + EQUATION_FILLED: 'equationFilled', + LEVEL_FINISHED: 'levelFinished', + NEXT_TASK: 'nextTask', + START: 'start' + } ); +} ); \ No newline at end of file diff --git a/js/common/model/ArithmeticModel.js b/js/common/model/ArithmeticModel.js index 7899efba..b07825d9 100644 --- a/js/common/model/ArithmeticModel.js +++ b/js/common/model/ArithmeticModel.js @@ -10,6 +10,7 @@ define( function( require ) { // modules var inherit = require( 'PHET_CORE/inherit' ); + var FaceModel = require( 'ARITHMETIC/common/model/FaceModel' ); var GameModel = require( 'ARITHMETIC/common/model/GameModel' ); var Property = require( 'AXON/Property' ); var PropertySet = require( 'AXON/PropertySet' ); @@ -24,6 +25,7 @@ define( function( require ) { var GameAudioPlayer = require( 'VEGAS/GameAudioPlayer' ); // constants + var GAME_STATE = require( 'ARITHMETIC/common/enum/GameState' ); var levels = [ // level 1 { @@ -59,7 +61,6 @@ define( function( require ) { scoreTotal: 0, // total user score for current games time: 0, // current time input: '', // user's input value - isLevelCompleted: false, // flag of level completing. If true - show statistic node isSound: true, // is sound active isTimer: false // is time mode active } ); @@ -70,6 +71,8 @@ define( function( require ) { // model for single game this.game = new GameModel( this.property( 'level' ), levels ); + this.smileFace = new FaceModel(); + // best times and scores, equal to number of levels this.bestTimes = []; this.bestScores = []; @@ -88,45 +91,55 @@ define( function( require ) { // init game after choosing level this.property( 'level' ).lazyLink( function( levelNumber ) { if ( levelNumber ) { - self.setTask(); + self.game.state = GAME_STATE.NEXT_TASK; } else { self.game.reset(); } } ); - this.game.property( 'multiplierLeft' ).lazyLink( this.checkNextTask.bind( this ) ); - this.game.property( 'multiplierRight' ).lazyLink( this.checkNextTask.bind( this ) ); - this.game.property( 'product' ).lazyLink( this.checkNextTask.bind( this ) ); - } - - return inherit( PropertySet, ArithmeticModel, { - checkNextTask: function() { - if ( this.checkAnswer() ) { - this.setTask(); - } - }, - checkAnswer: function() { - if ( this.game.multiplierLeft && this.game.multiplierRight && this.game.product ) { - + // set next task if equation was filled + this.game.property( 'state' ).lazyLink( function( state ) { + if ( state === GAME_STATE.EQUATION_FILLED ) { // show smile face - this.game.isFaceVisible = true; + self.smileFace.isVisible = true; // correct answer - if ( this.game.multiplierLeft * this.game.multiplierRight === this.game.product ) { - this.scoreTotal += this.game.scoreGame; - this.gameAudioPlayer.correctAnswer(); - return true; + if ( self.game.multiplierLeft * self.game.multiplierRight === self.game.product ) { + + // increase total score + self.scoreTotal += self.game.scoreGame; + + // set smile face view and play sound + self.smileFace.scoreFace = self.game.scoreGame; + self.smileFace.isSmile = true; + self.gameAudioPlayer.correctAnswer(); + + // mark answer in answer sheet + self.game.answerSheet[self.game.multiplierLeft - 1][self.game.multiplierRight - 1] = true; + + // set next task + self.game.state = GAME_STATE.NEXT_TASK; } // wrong answer else { - this.game.scoreGame = 0; - this.gameAudioPlayer.wrongAnswer(); - return false; + + // player will not get points for this task + self.game.scoreGame = 0; + + // set smile face view and play sound + self.smileFace.scoreFace = self.game.scoreGame; + self.smileFace.isSmile = false; + self.gameAudioPlayer.wrongAnswer(); + + // return to start state + self.game.state = GAME_STATE.START; } } - return false; - }, + } ); + } + + return inherit( PropertySet, ArithmeticModel, { checkInput: function() { // should be defined in child constructors }, @@ -136,9 +149,6 @@ define( function( require ) { reset: function() { PropertySet.prototype.reset.call( this ); }, - setTask: function() { - // should be defined in child constructors - }, step: function( dt ) { // if timer is on and level is select - add time if ( this.isTimer && this.level ) { diff --git a/js/common/model/FaceModel.js b/js/common/model/FaceModel.js new file mode 100644 index 00000000..ae6b6a21 --- /dev/null +++ b/js/common/model/FaceModel.js @@ -0,0 +1,24 @@ +// Copyright 2002-2013, University of Colorado Boulder + +/** + * Model for smile face in the 'Arithmetic' simulation. + * + * @author Andrey Zelenkov (MLearner) + */ +define( function( require ) { + 'use strict'; + + // modules + var inherit = require( 'PHET_CORE/inherit' ); + var PropertySet = require( 'AXON/PropertySet' ); + + function FaceModel() { + PropertySet.call( this, { + scoreFace: 1, // score near smile face + isVisible: false, // flag of smile face visibility + isSmile: true // flag of smile face emotion + } ); + } + + return inherit( PropertySet, FaceModel ); +} ); diff --git a/js/common/model/GameModel.js b/js/common/model/GameModel.js index e2cdde7a..0257b585 100644 --- a/js/common/model/GameModel.js +++ b/js/common/model/GameModel.js @@ -12,6 +12,9 @@ define( function( require ) { var inherit = require( 'PHET_CORE/inherit' ); var PropertySet = require( 'AXON/PropertySet' ); + // constants + var GAME_STATE = require( 'ARITHMETIC/common/enum/GameState' ); + function GameModel( levelProperty, levels ) { var self = this; @@ -19,8 +22,7 @@ define( function( require ) { multiplierLeft: undefined, // left multiplier multiplierRight: undefined, // right multiplier product: undefined, // product of multiplication - state: undefined, // current game state - isFaceVisible: false, // flag of smile face visibility + state: GAME_STATE.START, // current game state scoreGame: 1 // score for current game } ); @@ -41,43 +43,51 @@ define( function( require ) { // fill arrays appropriate to right multipliers self.answerSheet.forEach( function( el ) { _.times( answerSheetSize, function() { - el.push( false ); + el.push( true ); } ); } ); + + self.answerSheet[1][1] = false; } } ); } return inherit( PropertySet, GameModel, { reset: function() { + // reset properties PropertySet.prototype.reset.call( this ); }, // return available left and right multipliers according to answer sheet getAvailableMultipliers: function() { - var availableLeftMultipliers = []; - var availableRightMultipliers = []; + var availableMultipliersLeft = []; + var availableMultipliersRight = []; var multiplierLeft; var multiplierRight; // find available left multipliers this.answerSheet.forEach( function( rightMultipliers, index ) { if ( rightMultipliers.indexOf( false ) !== -1 ) { - availableLeftMultipliers.push( index + 1 ); + availableMultipliersLeft.push( index + 1 ); } } ); + // no more available multipliers + if ( !availableMultipliersLeft.length ) { + return null; + } + // set left multiplier - multiplierLeft = _.shuffle( availableLeftMultipliers )[0]; + multiplierLeft = _.shuffle( availableMultipliersLeft )[0]; // find available right multipliers this.answerSheet[multiplierLeft - 1].forEach( function( isRightMultiplierAnswered, index ) { if ( !isRightMultiplierAnswered ) { - availableRightMultipliers.push( index + 1 ); + availableMultipliersRight.push( index + 1 ); } } ); // set right multiplier - multiplierRight = _.shuffle( availableRightMultipliers )[0]; + multiplierRight = _.shuffle( availableMultipliersRight )[0]; return { multiplierLeft: multiplierLeft, diff --git a/js/common/view/EquationInputNode.js b/js/common/view/EquationInputNode.js index 9e7fe014..03327bbe 100644 --- a/js/common/view/EquationInputNode.js +++ b/js/common/view/EquationInputNode.js @@ -75,6 +75,11 @@ define( function( require ) { }, unfocus: function() { // TODO: stop blinking cursor + }, + reset: function(){ + this._inputText.setText( PLACEHOLDER ); + updateBoxPosition( this._box ); + this.disable(); } } ); } ); diff --git a/js/common/view/FaceWithScoreConfiguredNode.js b/js/common/view/FaceWithScoreConfiguredNode.js index dd7fbce8..2552c9ba 100644 --- a/js/common/view/FaceWithScoreConfiguredNode.js +++ b/js/common/view/FaceWithScoreConfiguredNode.js @@ -13,7 +13,7 @@ define( function( require ) { var FaceWithScoreNode = require( 'SCENERY_PHET/FaceWithScoreNode' ); var inherit = require( 'PHET_CORE/inherit' ); - function FaceWithScoreConfiguredNode( scoreGameProperty, isFaceVisibleProperty ) { + function FaceWithScoreConfiguredNode( smileFaceModel ) { var self = this; FaceWithScoreNode.call( this, { faceOpacity: 1, @@ -23,13 +23,19 @@ define( function( require ) { } ); // add observers - scoreGameProperty.link( function( scoreGame ) { - // set score and smile emotion + + // set score of smile face + smileFaceModel.property( 'scoreFace' ).link( function( scoreGame ) { self.setScore( scoreGame ); - self[scoreGame ? 'smile' : 'frown' ](); } ); - isFaceVisibleProperty.linkAttribute( self, 'visible' ); + // set smile face emotion + smileFaceModel.property( 'isSmile' ).link( function( isFaceSmile ) { + self[isFaceSmile ? 'smile' : 'frown' ](); + } ); + + // set visibility of smile face + smileFaceModel.property( 'isVisible' ).linkAttribute( self, 'visible' ); } return inherit( FaceWithScoreNode, FaceWithScoreConfiguredNode ); diff --git a/js/common/view/LevelCompletedConfiguredNode.js b/js/common/view/LevelCompletedConfiguredNode.js index cafd4413..c987f233 100644 --- a/js/common/view/LevelCompletedConfiguredNode.js +++ b/js/common/view/LevelCompletedConfiguredNode.js @@ -15,12 +15,15 @@ define( function( require ) { var LevelCompletedNode = require( 'VEGAS/LevelCompletedNode' ); var Node = require( 'SCENERY/nodes/Node' ); - function LevelCompletedConfiguredNode( levels, levelProperty, isLevelCompletedProperty, scoreTotalProperty, isTimerProperty, timeProperty, bestTimes ) { + // constants + var GAME_STATE = require( 'ARITHMETIC/common/enum/GameState' ); + + function LevelCompletedConfiguredNode( levels, levelProperty, stateProperty, scoreTotalProperty, isTimerProperty, timeProperty, bestTimes ) { var self = this; Node.call( this ); - isLevelCompletedProperty.lazyLink( function( isLevelCompleted ) { - if ( isLevelCompleted ) { + stateProperty.lazyLink( function( state ) { + if ( state === GAME_STATE.LEVEL_FINISHED ) { self.addChild( new LevelCompletedNode( levelProperty.value, scoreTotalProperty.value, diff --git a/js/common/view/WorkspaceNode.js b/js/common/view/WorkspaceNode.js index f097d66c..4d8ca7bf 100644 --- a/js/common/view/WorkspaceNode.js +++ b/js/common/view/WorkspaceNode.js @@ -57,8 +57,7 @@ define( function( require ) { // add smile face this.addChild( new FaceWithScoreConfiguredNode( - model.game.property( 'scoreGame' ), - model.game.property( 'isFaceVisible' ) + model.smileFace ).mutate( {bottom: layoutBounds.maxY * 0.95, left: layoutBounds.maxX * 0.04} ) ); @@ -66,7 +65,7 @@ define( function( require ) { this.addChild( new LevelCompletedConfiguredNode( model.levels, model.property( 'level' ), - model.property( 'isLevelCompleted' ), + model.game.property( 'state' ), model.property( 'scoreTotal' ), model.property( 'isTimer' ), model.property( 'time' ), diff --git a/js/common/view/table/MultiplicationTableNode.js b/js/common/view/table/MultiplicationTableNode.js index d2595e03..f7e5d125 100644 --- a/js/common/view/table/MultiplicationTableNode.js +++ b/js/common/view/table/MultiplicationTableNode.js @@ -19,9 +19,10 @@ define( function( require ) { var VBox = require( 'SCENERY/nodes/VBox' ); // constants + var GAME_STATE = require( 'ARITHMETIC/common/enum/GameState' ); var TABLE_SIZE = Constants.SIZE; - function MultiplicationTableNode( levelProperty, levels ) { + function MultiplicationTableNode( levelProperty, levels, gameModel ) { var self = this; Node.call( this ); @@ -102,6 +103,18 @@ define( function( require ) { self.clearCells( levelNumberPrev ); } } ); + + gameModel.property( 'state' ).link( function( state ) { + if ( state === GAME_STATE.NEXT_TASK ) { + gameModel.answerSheet.forEach( function( multipliersLeft, multipliersLeftIndex ) { + multipliersLeft.forEach( function( isVisible, multipliersRightIndex ) { + if ( isVisible ) { + self.cells[levelProperty.value - 1][multipliersLeftIndex + 1][multipliersRightIndex + 1].showText(); + } + } ); + } ); + } + } ); } return inherit( Node, MultiplicationTableNode, { diff --git a/js/divide/model/DivideModel.js b/js/divide/model/DivideModel.js index c657d4ee..f1c54912 100644 --- a/js/divide/model/DivideModel.js +++ b/js/divide/model/DivideModel.js @@ -22,15 +22,17 @@ define( function( require ) { // get available multipliers var multipliers = this.game.getAvailableMultipliers(); - // set product - this.game.product = multipliers.multiplierLeft * multipliers.multiplierRight; + if ( multipliers ) { + // set product + this.game.product = multipliers.multiplierLeft * multipliers.multiplierRight; - // set left or right multiplier - if ( Math.random() < 0.5 ) { - this.game.multiplierLeft = multipliers.multiplierLeft; - } - else { - this.game.multiplierRight = multipliers.multiplierRight; + // set left or right multiplier + if ( Math.random() < 0.5 ) { + this.game.multiplierLeft = multipliers.multiplierLeft; + } + else { + this.game.multiplierRight = multipliers.multiplierRight; + } } } } ); diff --git a/js/divide/view/MultiplicationTableDivideNode.js b/js/divide/view/MultiplicationTableDivideNode.js index a6f79c5a..c03157ea 100644 --- a/js/divide/view/MultiplicationTableDivideNode.js +++ b/js/divide/view/MultiplicationTableDivideNode.js @@ -15,7 +15,7 @@ define( function( require ) { function MultiplicationTableDivideNode( gameModel, levelProperty, levels ) { var self = this; - MultiplicationTableNode.call( this, levelProperty, levels ); + MultiplicationTableNode.call( this, levelProperty, levels, gameModel ); // set left multiplier selected gameModel.property( 'multiplierLeft' ).lazyLink( function() { diff --git a/js/factor/model/FactorModel.js b/js/factor/model/FactorModel.js index f429e205..96dec295 100644 --- a/js/factor/model/FactorModel.js +++ b/js/factor/model/FactorModel.js @@ -13,21 +13,41 @@ define( function( require ) { var inherit = require( 'PHET_CORE/inherit' ); var ArithmeticModel = require( 'ARITHMETIC/common/model/ArithmeticModel' ); + // constants + var GAME_STATE = require( 'ARITHMETIC/common/enum/GameState' ); + function FactorModel() { + var self = this; ArithmeticModel.call( this ); - } - return inherit( ArithmeticModel, FactorModel, { - setTask: function() { - // get available multipliers - var multipliers = this.game.getAvailableMultipliers(); + this.game.property( 'state' ).link( function( state ) { + console.log( state ); + } ); + + // next task observer + this.game.property( 'state' ).link( function( state ) { + if ( state === GAME_STATE.NEXT_TASK ) { + // get available multipliers + var multipliers = self.game.getAvailableMultipliers(); - // set product - this.game.property( 'multiplierLeft' ).reset(); - this.game.property( 'multiplierRight' ).reset(); - this.game.product = multipliers.multiplierLeft * multipliers.multiplierRight; + if ( multipliers ) { + // reset multipliers and score properties + self.game.property( 'multiplierLeft' ).reset(); + self.game.property( 'multiplierRight' ).reset(); + self.game.property( 'scoreGame' ).reset(); + + // set product + self.game.product = multipliers.multiplierLeft * multipliers.multiplierRight; + + // set start state + self.game.state = GAME_STATE.START; + } + else { + self.game.state = GAME_STATE.LEVEL_FINISHED; + } + } + } ); + } - this.game.property( 'scoreGame' ).reset(); - } - } ); + return inherit( ArithmeticModel, FactorModel ); } ); diff --git a/js/factor/view/EquationFactorNode.js b/js/factor/view/EquationFactorNode.js index 7420a171..324ec90b 100644 --- a/js/factor/view/EquationFactorNode.js +++ b/js/factor/view/EquationFactorNode.js @@ -12,8 +12,19 @@ define( function( require ) { var EquationNode = require( 'ARITHMETIC/common/view/EquationNode' ); var inherit = require( 'PHET_CORE/inherit' ); - function EquationFactorNode( multiplierLeftProperty, multiplierRightProperty, productProperty, inputProperty ) { + // constants + var GAME_STATE = require( 'ARITHMETIC/common/enum/GameState' ); + + function EquationFactorNode( stateProperty, multiplierLeftProperty, multiplierRightProperty, productProperty, inputProperty ) { + var self = this; EquationNode.call( this, multiplierLeftProperty, multiplierRightProperty, productProperty, inputProperty ); + + stateProperty.link( function( state ) { + if ( state === GAME_STATE.START ) { + self.multiplierLeftInput.reset(); + self.multiplierRightInput.reset(); + } + } ); } return inherit( EquationNode, EquationFactorNode ); diff --git a/js/factor/view/FactorScreen.js b/js/factor/view/FactorScreen.js index 4daa8adc..d7f60016 100644 --- a/js/factor/view/FactorScreen.js +++ b/js/factor/view/FactorScreen.js @@ -31,6 +31,7 @@ define( function( require ) { model.levels ), new EquationFactorNode( + model.game.property( 'state' ), model.game.property( 'multiplierLeft' ), model.game.property( 'multiplierRight' ), model.game.property( 'product' ), diff --git a/js/factor/view/MultiplicationTableFactorNode.js b/js/factor/view/MultiplicationTableFactorNode.js index 74951b64..a36a8354 100644 --- a/js/factor/view/MultiplicationTableFactorNode.js +++ b/js/factor/view/MultiplicationTableFactorNode.js @@ -11,14 +11,16 @@ define( function( require ) { // modules var ButtonListener = require( 'SUN/buttons/ButtonListener' ); - var PushButtonModel = require( 'SUN/buttons/PushButtonModel' ); - var inherit = require( 'PHET_CORE/inherit' ); var MultiplicationTableNode = require( 'ARITHMETIC/common/view/table/MultiplicationTableNode' ); + var PushButtonModel = require( 'SUN/buttons/PushButtonModel' ); + + // constants + var GAME_STATE = require( 'ARITHMETIC/common/enum/GameState' ); function MultiplicationTableFactorNode( gameModel, levelProperty, levels ) { var self = this; - MultiplicationTableNode.call( this, levelProperty, levels ); + MultiplicationTableNode.call( this, levelProperty, levels, gameModel ); // add 'hover' and 'down' listeners for each cell in table this.cells.forEach( function( tableForLevel ) { @@ -49,6 +51,8 @@ define( function( require ) { buttonModel.property( 'down' ).onValue( true, function() { gameModel.multiplierLeft = leftIndex; gameModel.multiplierRight = rightIndex; + + gameModel.state = GAME_STATE.EQUATION_FILLED; } ); } } ); diff --git a/js/multiply/model/MultiplyModel.js b/js/multiply/model/MultiplyModel.js index bef10af7..ccdaba08 100644 --- a/js/multiply/model/MultiplyModel.js +++ b/js/multiply/model/MultiplyModel.js @@ -25,9 +25,11 @@ define( function( require ) { // get available multipliers var multipliers = this.game.getAvailableMultipliers(); - // set left and right multipliers - this.game.multiplierLeft = multipliers.multiplierLeft; - this.game.multiplierRight = multipliers.multiplierRight; + if ( multipliers ) { + // set left and right multipliers + this.game.multiplierLeft = multipliers.multiplierLeft; + this.game.multiplierRight = multipliers.multiplierRight; + } } } ); } ); diff --git a/js/multiply/view/MultiplicationTableMultiplyNode.js b/js/multiply/view/MultiplicationTableMultiplyNode.js index 8534bc86..6c1206b6 100644 --- a/js/multiply/view/MultiplicationTableMultiplyNode.js +++ b/js/multiply/view/MultiplicationTableMultiplyNode.js @@ -15,7 +15,7 @@ define( function( require ) { function MultiplicationTableMultiplyNode( gameModel, levelProperty, levels ) { var self = this; - MultiplicationTableNode.call( this, levelProperty, levels ); + MultiplicationTableNode.call( this, levelProperty, levels, gameModel ); // set view for multiplication table after choosing left and right multipliers gameModel.property( 'multiplierRight' )