Skip to content

Commit

Permalink
Add preferences model and structure for character sets. See #198.
Browse files Browse the repository at this point in the history
  • Loading branch information
Luisav1 committed Nov 27, 2023
1 parent da2c96a commit c561c75
Show file tree
Hide file tree
Showing 15 changed files with 269 additions and 50 deletions.
5 changes: 4 additions & 1 deletion arithmetic_en.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@
"phet-io"
],
"simFeatures": {
"supportsSound": true
"supportsSound": true,
"supportedRegionsAndCultures": [
"usa"
]
},
"simulation": true,
"phet-io": {
Expand Down
15 changes: 12 additions & 3 deletions js/arithmetic-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
* @author John Blanco, Andrey Zelenkov (MLearner)
*/

import PreferencesModel from '../../joist/js/preferences/PreferencesModel.js';
import Sim from '../../joist/js/Sim.js';
import simLauncher from '../../joist/js/simLauncher.js';
import Tandem from '../../tandem/js/Tandem.js';
import ArithmeticStrings from './ArithmeticStrings.js';
import BoxPlayerImages from './common/view/BoxPlayerImages.js';
import DivideScreen from './divide/DivideScreen.js';
import FactorScreen from './factor/FactorScreen.js';
import MultiplyScreen from './multiply/MultiplyScreen.js';
Expand All @@ -20,7 +22,14 @@ const arithmeticTitleStringProperty = ArithmeticStrings.arithmetic.titleStringPr
// constants
const tandem = Tandem.ROOT;

const preferencesModel = new PreferencesModel( {
localizationOptions: {
characterSets: BoxPlayerImages.BOX_PLAYER_CHARACTER_SETS
}
} );

const simOptions = {
preferencesModel: preferencesModel,
credits: {
leadDesign: 'Michael Dubson, Amanda McGarry',
softwareDevelopment: 'John Blanco, Michael Dubson',
Expand All @@ -33,8 +42,8 @@ const simOptions = {
simLauncher.launch( () => {
// Create and start the sim
new Sim( arithmeticTitleStringProperty, [
new MultiplyScreen( { tandem: tandem.createTandem( 'multiplyScreen' ) } ),
new FactorScreen( { tandem: tandem.createTandem( 'factorScreen' ) } ),
new DivideScreen( { tandem: tandem.createTandem( 'divideScreen' ) } )
new MultiplyScreen( preferencesModel, { tandem: tandem.createTandem( 'multiplyScreen' ) } ),
new FactorScreen( preferencesModel, { tandem: tandem.createTandem( 'factorScreen' ) } ),
new DivideScreen( preferencesModel, { tandem: tandem.createTandem( 'divideScreen' ) } )
], simOptions ).start();
} );
8 changes: 7 additions & 1 deletion js/common/model/ArithmeticModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class ArithmeticModel {
/**
* Constructor for ArithmeticModel
*/
constructor( tandem, options ) {
constructor( preferencesModel, tandem, options ) {

// @private - for PhET-iO
this.checkAnswerEmitter = new Emitter( {
Expand Down Expand Up @@ -92,6 +92,12 @@ class ArithmeticModel {
this.activeLevelModel.displayScoreProperty.set( this.activeLevelModel.currentScoreProperty.get() );
}
} );

/**
* @public
* @type {Property<RegionAndCulturePortrayal>}
*/
this.regionAndCulturePortrayalProperty = preferencesModel.localizationModel.regionAndCulturePortrayalProperty;
}

// @protected - get the current level model, use this to make the code more readable
Expand Down
48 changes: 48 additions & 0 deletions js/common/view/BoxPlayerCharacterSet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2023, University of Colorado Boulder

/**
* The BoxPlayerCharacterSet defines what is needed for each character set in Arithmetic.
*
* @author Luisa Vargas
*
*/

import RegionAndCulturePortrayal from '../../../../joist/js/preferences/RegionAndCulturePortrayal.js';
import arithmetic from '../../arithmetic.js';

export default class BoxPlayerCharacterSet extends RegionAndCulturePortrayal {

/**
* @param label { LocalizedStringProperty }
* @param multiplyLevel1 { HTMLImageElement }
* @param multiplyLevel2 { HTMLImageElement }
* @param multiplyLevel3 { HTMLImageElement }
* @param factorLevel1 { HTMLImageElement }
* @param factorLevel2 { HTMLImageElement }
* @param factorLevel3 { HTMLImageElement }
* @param divideLevel1 { HTMLImageElement }
* @param divideLevel2 { HTMLImageElement }
* @param divideLevel3 { HTMLImageElement }
* @param queryParameterValue { string }
*/
constructor( label,
multiplyLevel1, multiplyLevel2, multiplyLevel3,
factorLevel1, factorLevel2, factorLevel3,
divideLevel1, divideLevel2, divideLevel3,
queryParameterValue ) {

super( label, queryParameterValue, {} );

this.multiplyLevel1 = multiplyLevel1;
this.multiplyLevel2 = multiplyLevel2;
this.multiplyLevel3 = multiplyLevel3;
this.factorLevel1 = factorLevel1;
this.factorLevel2 = factorLevel2;
this.factorLevel3 = factorLevel3;
this.divideLevel1 = divideLevel1;
this.divideLevel2 = divideLevel2;
this.divideLevel3 = divideLevel3;
}
}

arithmetic.register( 'BoxPlayerCharacterSet', BoxPlayerCharacterSet );
37 changes: 37 additions & 0 deletions js/common/view/BoxPlayerCharacterSetUSA.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2023, University of Colorado Boulder

/**
* This file instantiates the USA region and culture portrayals.
*
* @author Luisa Vargas
*
*/

import JoistStrings from '../../../../joist/js/JoistStrings.js';
import { USA_REGION_AND_CULTURE_ID } from '../../../../joist/js/preferences/RegionAndCulturePortrayal.js';
import divideLevel1Icon_png from '../../../mipmaps/divideLevel1Icon_png.js';
import divideLevel2Icon_png from '../../../mipmaps/divideLevel2Icon_png.js';
import divideLevel3Icon_png from '../../../mipmaps/divideLevel3Icon_png.js';
import factorLevel1Icon_png from '../../../mipmaps/factorLevel1Icon_png.js';
import factorLevel2Icon_png from '../../../mipmaps/factorLevel2Icon_png.js';
import factorLevel3Icon_png from '../../../mipmaps/factorLevel3Icon_png.js';
import multiplyLevel1Icon_png from '../../../mipmaps/multiplyLevel1Icon_png.js';
import multiplyLevel2Icon_png from '../../../mipmaps/multiplyLevel2Icon_png.js';
import multiplyLevel3Icon_png from '../../../mipmaps/multiplyLevel3Icon_png.js';
import BoxPlayerCharacterSet from './BoxPlayerCharacterSet.js';

const ExplorerCharacterSetUSA = new BoxPlayerCharacterSet(
JoistStrings.preferences.tabs.localization.regionAndCulture.portrayalSets.unitedStatesOfAmericaStringProperty,
multiplyLevel1Icon_png,
multiplyLevel2Icon_png,
multiplyLevel3Icon_png,
factorLevel1Icon_png,
factorLevel2Icon_png,
factorLevel3Icon_png,
divideLevel1Icon_png,
divideLevel2Icon_png,
divideLevel3Icon_png,
USA_REGION_AND_CULTURE_ID
);

export default ExplorerCharacterSetUSA;
105 changes: 105 additions & 0 deletions js/common/view/BoxPlayerController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2023, University of Colorado Boulder

/**
* The BoxPlayerController creates the images of each version of the 'level' box player ( multiply level 1,
* multiply level 2, multiply level 3, factor level 1, factor level 2, factor level 3, divide level 1, divide level 2,
* and divide level 3), as well as defines the visibility of each individual image based on the
* regionAndCulturePortrayalProperty.
*
* @author Luisa Vargas
*
*/

import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
import { Image, Node } from '../../../../scenery/js/imports.js';
import arithmetic from '../../arithmetic.js';
import BoxPlayerImages from './BoxPlayerImages.js';

export default class BoxPlayerController {

/**
* @param { ArithmeticModel } sceneModel
*/
constructor( sceneModel ) {

const boxPlayerSets = BoxPlayerImages.BOX_PLAYER_CHARACTER_SETS;
const regionAndCulturePortrayalProperty = sceneModel.regionAndCulturePortrayalProperty;

const createVisibleProperty = set => {
return new DerivedProperty( [ regionAndCulturePortrayalProperty ], portrayal => {
return portrayal === set;
} );
};

const multiplyLevel1Images = boxPlayerSets.map( set => new Image( set.multiplyLevel1,
{
visibleProperty: createVisibleProperty( set )
} ) );
const multiplyLevel2Images = boxPlayerSets.map( set => new Image( set.multiplyLevel2,
{
visibleProperty: createVisibleProperty( set )
} ) );
const multiplyLevel3Images = boxPlayerSets.map( set => new Image( set.multiplyLevel3,
{
visibleProperty: createVisibleProperty( set )
} ) );
const factorLevel1Images = boxPlayerSets.map( set => new Image( set.factorLevel1,
{
visibleProperty: createVisibleProperty( set )
} ) );
const factorLevel2Images = boxPlayerSets.map( set => new Image( set.factorLevel2,
{
visibleProperty: createVisibleProperty( set )
} ) );
const factorLevel3Images = boxPlayerSets.map( set => new Image( set.factorLevel3,
{
visibleProperty: createVisibleProperty( set )
} ) );
const divideLevel1Images = boxPlayerSets.map( set => new Image( set.divideLevel1,
{
visibleProperty: createVisibleProperty( set )
} ) );
const divideLevel2Images = boxPlayerSets.map( set => new Image( set.divideLevel2,
{
visibleProperty: createVisibleProperty( set )
} ) );
const divideLevel3Images = boxPlayerSets.map( set => new Image( set.divideLevel3,
{
visibleProperty: createVisibleProperty( set )
} ) );

const multiplyLevel1Node = new Node( { children: multiplyLevel1Images } );
const multiplyLevel2Node = new Node( { children: multiplyLevel2Images } );
const multiplyLevel3Node = new Node( { children: multiplyLevel3Images } );
const factorLevel1Node = new Node( { children: factorLevel1Images } );
const factorLevel2Node = new Node( { children: factorLevel2Images } );
const factorLevel3Node = new Node( { children: factorLevel3Images } );
const divideLevel1Node = new Node( { children: divideLevel1Images } );
const divideLevel2Node = new Node( { children: divideLevel2Images } );
const divideLevel3Node = new Node( { children: divideLevel3Images } );

/**
* @public
* @type {{divide: Node[], multiply: Node[], factor: Node[]}}
*/
this.boxPlayerNodes = {
multiply: [
multiplyLevel1Node,
multiplyLevel2Node,
multiplyLevel3Node
],
factor: [
factorLevel1Node,
factorLevel2Node,
factorLevel3Node
],
divide: [
divideLevel1Node,
divideLevel2Node,
divideLevel3Node
]
};
}
}

arithmetic.register( 'BoxPlayerController', BoxPlayerController );
20 changes: 20 additions & 0 deletions js/common/view/BoxPlayerImages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2023, University of Colorado Boulder
/**
* BoxPlayer images contains an array of character sets, each representing a different region/culture.
*
* @author Luisa Vargas
*
*/

import arithmetic from '../../arithmetic.js';
import BoxPlayerCharacterSetUSA from './BoxPlayerCharacterSetUSA.js';


const BoxPlayerImages = {
BOX_PLAYER_CHARACTER_SETS: [
BoxPlayerCharacterSetUSA
]
};

arithmetic.register( 'BoxPlayerImages', BoxPlayerImages );
export default BoxPlayerImages;
41 changes: 10 additions & 31 deletions js/common/view/LevelSelectionNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,20 @@ import merge from '../../../../phet-core/js/merge.js';
import ResetAllButton from '../../../../scenery-phet/js/buttons/ResetAllButton.js';
import TimerToggleButton from '../../../../scenery-phet/js/buttons/TimerToggleButton.js';
import PhetFont from '../../../../scenery-phet/js/PhetFont.js';
import { HBox, Image, Node, Text, VBox } from '../../../../scenery/js/imports.js';
import { HBox, Node, Text, VBox } from '../../../../scenery/js/imports.js';
import LevelSelectionButton from '../../../../vegas/js/LevelSelectionButton.js';
import ScoreDisplayStars from '../../../../vegas/js/ScoreDisplayStars.js';
import divideLevel1Icon_png from '../../../mipmaps/divideLevel1Icon_png.js';
import divideLevel2Icon_png from '../../../mipmaps/divideLevel2Icon_png.js';
import divideLevel3Icon_png from '../../../mipmaps/divideLevel3Icon_png.js';
import factorLevel1Icon_png from '../../../mipmaps/factorLevel1Icon_png.js';
import factorLevel2Icon_png from '../../../mipmaps/factorLevel2Icon_png.js';
import factorLevel3Icon_png from '../../../mipmaps/factorLevel3Icon_png.js';
import multiplyLevel1Icon_png from '../../../mipmaps/multiplyLevel1Icon_png.js';
import multiplyLevel2Icon_png from '../../../mipmaps/multiplyLevel2Icon_png.js';
import multiplyLevel3Icon_png from '../../../mipmaps/multiplyLevel3Icon_png.js';
import arithmetic from '../../arithmetic.js';
import ArithmeticStrings from '../../ArithmeticStrings.js';
import ArithmeticConstants from '../ArithmeticConstants.js';
import ArithmeticGlobals from '../ArithmeticGlobals.js';
import BoxPlayerController from './BoxPlayerController.js';

// constants
const CHOOSE_LEVEL_TITLE_FONT = new PhetFont( { size: 24 } );
const TAB_TITLE_FONT = new PhetFont( { size: 54 } );
const BUTTON_LENGTH = 150;

// icon sets, used to place on the buttons
const ICON_SETS = {
multiply: [
multiplyLevel1Icon_png,
multiplyLevel2Icon_png,
multiplyLevel3Icon_png
],
factor: [
factorLevel1Icon_png,
factorLevel2Icon_png,
factorLevel3Icon_png
],
divide: [
divideLevel1Icon_png,
divideLevel2Icon_png,
divideLevel3Icon_png
]
};

const chooseYourLevelString = ArithmeticStrings.chooseYourLevel;

class LevelSelectionNode extends Node {
Expand Down Expand Up @@ -88,10 +61,16 @@ class LevelSelectionNode extends Node {
} );
this.addChild( chooseLevelTitle );


const boxPlayerController = new BoxPlayerController( model );

// icon sets, used to place on the buttons
const iconSets = boxPlayerController.boxPlayerNodes;

// add select level buttons
assert && assert( model.levelModels.length === ICON_SETS[ options.iconSet ].length, 'Number of icons doesn\'t match number of levels' );
assert && assert( model.levelModels.length === iconSets[ options.iconSet ].length, 'Number of icons doesn\'t match number of levels' );
const levelSelectButtons = model.levelModels.map( ( level, levelIndex ) => new LevelSelectionButton(
new Image( ICON_SETS[ options.iconSet ][ levelIndex ] ),
iconSets[ options.iconSet ][ levelIndex ],
model.levelModels[ levelIndex ].displayScoreProperty,
{
buttonWidth: BUTTON_LENGTH,
Expand Down
5 changes: 3 additions & 2 deletions js/divide/DivideScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import DivideView from './view/DivideView.js';
class DivideScreen extends Screen {

/**
* @param { PreferencesModel } preferencesModel
* @param {Object} [options]
*/
constructor( options ) {
constructor( preferencesModel, options ) {

options = merge( {
name: ArithmeticStrings.divideStringProperty,
Expand All @@ -37,7 +38,7 @@ class DivideScreen extends Screen {
}, options );

super(
() => new DivideModel( options.tandem.createTandem( 'model' ) ),
() => new DivideModel( preferencesModel, options.tandem.createTandem( 'model' ) ),
model => new DivideView( model ),
options
);
Expand Down
7 changes: 4 additions & 3 deletions js/divide/model/DivideModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import GameState from '../../common/model/GameState.js';
class DivideModel extends ArithmeticModel {

/**
* @param {Tandem} tandem
* @param { PreferencesModel } preferencesModel
* @param { Tandem } tandem
*/
constructor( tandem ) {
super( tandem, {
constructor( preferencesModel, tandem ) {
super( preferencesModel, tandem, {
fillEquation: () => {

// Convert any strings entered by the user into numerical values.
Expand Down
Loading

0 comments on commit c561c75

Please sign in to comment.