From c6b505b5b956d3325af8460f49fe9b5e45c6c59d Mon Sep 17 00:00:00 2001 From: ameliajmoser <77028687+ameliajmoser@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:01:36 -0600 Subject: [PATCH] add sequence step data, remove block.id --- web/src/components/blockSequence.js | 6 ++-- web/src/components/levelContainer.js | 22 +++++++------- web/src/components/levelSelector.js | 4 +-- web/src/model/blocks.js | 20 +++++++++++-- web/src/model/levelModel.js | 45 ++++++++++++++++++++++++---- 5 files changed, 70 insertions(+), 27 deletions(-) diff --git a/web/src/components/blockSequence.js b/web/src/components/blockSequence.js index 70aed3d..045ae6a 100644 --- a/web/src/components/blockSequence.js +++ b/web/src/components/blockSequence.js @@ -45,7 +45,7 @@ const LoopStep = ({ block, setEditLoop, focusLoop, removeInstruction, updateInst onChange={(e) => { if (block.isAmountValid()) { let old = block.amounts[0]; - logEvent('set_block_parameter', {'block_id': block.id, 'block_type': block.type, 'changed_param': block.description[0], 'old_value': old, 'new_value': e.target.value}) + logEvent('set_block_parameter', {'block_type': block.type, 'changed_param': block.description[0], 'old_value': old, 'new_value': e.target.value}) } updateInstruction(block, new LoopBlock({ ...block, amounts: [e.target.value] })) }} @@ -71,7 +71,7 @@ const SingleStep = ({ block, removeInstruction, updateInstruction, disabled, ind let old = amounts[i]; amounts[i] = e.target.value if (block.isAmountValid()) { - logEvent('set_block_parameter', {'block_id': block.id, 'block_type': block.type, 'changed_param': block.description[i], 'old_value': old, 'new_value': e.target.value}) + logEvent('set_block_parameter', {'block_type': block.type, 'changed_param': block.description[i], 'old_value': old, 'new_value': e.target.value}) } return updateInstruction(block, new Block({ ...block, amounts })) }} @@ -123,7 +123,7 @@ const BlockSequence = ({ blocks, focusLoop, reorderInstructions, executeInstruct function reorderInstructionArray(blocks, moveFrom, moveTo, inLoop) { const [removed] = blocks.splice(moveFrom, 1) console.log(removed); - logEvent("drag_block", {"block_id": removed.id, "from_index": moveFrom, "new_index": moveTo, "in_loop": inLoop, "loop_id": undefined, "block_type": removed.type, "block_params": removed.paramMap}) + logEvent("drag_block", {"from_index": moveFrom, "new_index": moveTo, "in_loop": inLoop, "loop_id": undefined, "block_type": removed.type, "block_params": removed.paramMap}) logEvent("sequence_updated", {'sequence_elements': getSequenceData(blocks)}) blocks.splice(moveTo, 0, removed) return blocks diff --git a/web/src/components/levelContainer.js b/web/src/components/levelContainer.js index 80eb78b..355bfd3 100644 --- a/web/src/components/levelContainer.js +++ b/web/src/components/levelContainer.js @@ -105,16 +105,15 @@ const LevelContainer = ({ afterExecute, ...props }) => { const nextModelView = models => { if (models.length) { if (models.length === 1 && afterExecute) afterExecute(models[0]) - setLevelModel(models[0]) - console.warn("TODO sequence step: type, outcome"); - console.log(levelModel.step); + let newModel = models[0] + setLevelModel(newModel) logEvent("sequence_execution_step", { - 'type': levelModel.step, - 'moves_count': levelModel.numberOfMoves + 1, - 'blue_gems': levelModel.numberOfGemsCollected(REWARDS.blue), - 'yellow_gems': levelModel.numberOfGemsCollected(REWARDS.yellow), - 'stamp_points': levelModel.getStampScore(), - 'outcome': null}); + 'type': newModel.stepType, + 'moves_count': newModel.numberOfMoves, + 'blue_gems': newModel.numberOfGemsCollected(REWARDS.blue), + 'yellow_gems': newModel.numberOfGemsCollected(REWARDS.yellow), + 'stamp_points': newModel.getStampScore(), + 'outcome': newModel.judgeOutcome()}); setExecutionView(models.filter((_, i) => i > 0)) } } @@ -177,19 +176,18 @@ const LevelContainer = ({ afterExecute, ...props }) => { let editLoop = filterBlock(levelModel.editLoop) ? levelModel.editLoop : undefined let inLoop = false; - let loopId; if (blockIndex !== -1) { blocks = new BlockQueue(levelModel.blockQueue.queue.filter(filterBlock)) setLevelModel(new LevelModel({ ...levelModel, blockQueue: blocks, editLoop })) + blocks = blocks.queue } else { const newModel = checkForLoopsContainingBlock(blocks) inLoop = true; - loopId = editLoop && editLoop.id; setLevelModel(new LevelModel({ ...newModel})) } - logEvent("delete_block", {"block_id": rBlock.id, "block_index": blockIndex, "in_loop": inLoop, "loop_id": loopId, "block_type": rBlock.type, "block_params": rBlock.paramMap}) + logEvent("delete_block", {"block_index": blockIndex, "in_loop": inLoop, "block_type": rBlock.type, "block_params": rBlock.paramMap}) logEvent("sequence_updated", {'sequence_elements': getSequenceData(blocks)}) } diff --git a/web/src/components/levelSelector.js b/web/src/components/levelSelector.js index 122e8c5..e204d71 100644 --- a/web/src/components/levelSelector.js +++ b/web/src/components/levelSelector.js @@ -76,11 +76,9 @@ const LevelSelector = ({ levelProgression }) => { logEvent("navigation_displayed"); console.warn("TODO: navigation display, all available levels"); + const onClickLevel = (number, acquiredMedals) => { // console.log(levelProgression.levels); - console.log(acquiredMedals); - console.log("level clicked, got level " + number + " with " + [...acquiredMedals]); - // TODO: verify shield fetch logEvent("select_level", {level: number, level_shields: [...acquiredMedals]}); history.push('/level/' + number) } diff --git a/web/src/model/blocks.js b/web/src/model/blocks.js index 8630f49..dee636c 100644 --- a/web/src/model/blocks.js +++ b/web/src/model/blocks.js @@ -27,7 +27,12 @@ export class BlockQueue { } getStepsFromQueue() { - return _.flatMap(this.queue, block => block.getTransformSteps()) + let transforms = _.flatMap(this.queue, block => block.getTransformSteps()) + let types = _.flatMap(this.queue, block => block.getStepType()) + return { + 'transforms': transforms, + 'types': types, + } } } @@ -41,7 +46,7 @@ export const getSequenceData = blockQueue => { export const getBlockData = block => { let data = {} - data.block_id = block.id; + // data.block_id = block.id; data.block_type = block.type; if (block.type === BLOCK_TYPES.repeat) { data.loop_subelements = getSequenceData(block.blockQueue.queue); @@ -103,6 +108,10 @@ export class Block { return [this.transformGenerator(...this.amounts.map(a => Number(a)))] } + getStepType() { + return this.type; + } + isAmountValid() { return this.amounts.map(a => a + '').every(a => a.match(/^-?[0-9]+$/g)) } @@ -130,7 +139,11 @@ export class LoopBlock { } getTransformSteps() { - return _.flatMap(_.range(this.amounts[0]), () => this.blockQueue.getStepsFromQueue()) + return _.flatMap(_.range(this.amounts[0]), () => this.blockQueue.getStepsFromQueue().transforms) + } + + getStepType() { + return _.flatMap(_.range(this.amounts[0]), () => this.blockQueue.getStepsFromQueue().types); } addBlock(block) { @@ -216,3 +229,4 @@ export const createReflectYWithLine = () => new Block({ }) export const createLoop = iterations => new LoopBlock({ amounts: [iterations] }) + diff --git a/web/src/model/levelModel.js b/web/src/model/levelModel.js index 2371a58..3f18e27 100644 --- a/web/src/model/levelModel.js +++ b/web/src/model/levelModel.js @@ -61,6 +61,7 @@ export default class LevelModel { this.description = obj.description this.creative = obj.creative this.blockQueue = obj.blockQueue || new BlockQueue() + this.stepType = obj.stepType this.executionStep = obj.executionStep this.editLoop = obj.editLoop this.endGoal = obj.endGoal @@ -102,6 +103,7 @@ export default class LevelModel { const [contains, excludes] = _.partition(obj.rewards || [], reward => this.playerToken.doesContain(reward)) this.rewards = excludes this.collectedRewards = contains.concat(obj.collectedRewards || []) + this.newRewards = contains || []; this.medalCriteria = obj.medalCriteria || [] this.acquiredMedals = obj.acquiredMedals || [] this.complete = obj.complete || this.obstacleHit || this.error @@ -126,7 +128,7 @@ export default class LevelModel { addBlock(block) { logEvent("select_new_block", {"block_type": block.type, "block_params": block.paramMap}) if (block.type !== BLOCK_TYPES.repeat && !this.availableBlocks.includes(block.type)) { - throw new Error('Bad, you are not allowed to add a block of type: ' + block.id + ' because this level model only has these allowed blocks: ' + this.availableBlocks) + throw new Error('Bad, you are not allowed to add a block of type: ' + block.type + ' because this level model only has these allowed blocks: ' + this.availableBlocks) } if (this.editLoop && !this.blockQueue.queue.includes(this.editLoop)) { // editLoop exists but is not in the queue - don't add anything to it @@ -134,20 +136,18 @@ export default class LevelModel { } let blockQueue = this.blockQueue; let inLoop = false; - let loopId; let blockIndex = -1; if (this.editLoop) { this.editLoop.addBlock(block); inLoop = true; - loopId = this.editLoop.id blockIndex = this.editLoop.blockQueue.queue.indexOf(block); } else { blockQueue = this.blockQueue.addBlock(block); blockIndex = blockQueue.queue.indexOf(block) } - logEvent("add_new_block", {"block_id": block.id, "block_index": blockIndex, "in_loop": inLoop, "loop_id": loopId, "block_type": block.type, "block_params": block.paramMap}) + logEvent("add_new_block", {"block_index": blockIndex, "in_loop": inLoop, "block_type": block.type, "block_params": block.paramMap}) logEvent("sequence_updated", {'sequence_elements': getSequenceData(blockQueue.queue)}) return new LevelModel({ ...this, blockQueue, editLoop: block.type === BLOCK_TYPES.repeat ? block : this.editLoop }) } @@ -168,6 +168,10 @@ export default class LevelModel { return REWARDS[type] && this.collectedRewards.filter((reward) => reward.type === type).length } + gemJustCollected(type) { + return REWARDS[type] && this.newRewards.some((reward) => reward.type === type) + } + getStampScore() { return this.stamps.reduce((score, nextStamp) => (nextStamp.collected ? (score + nextStamp.value) : score), 0) } @@ -188,6 +192,22 @@ export default class LevelModel { return this.blockQueue.queue.filter(block => block instanceof type).length } + judgeOutcome() { + if (this.won) return OUTCOME.GOAL; + if (this.obstacleHit) return OUTCOME.MONSTER; + if (this.error) return OUTCOME.OUT_OF_BOUNDS; + if (this.newRewards) { + if (this.gemJustCollected(REWARDS.blue)) return OUTCOME.GEM_BLUE; + if (this.gemJustCollected(REWARDS.yellow)) return OUTCOME.GEM_YELLOW; + } + let newStamp = this.stamps.find(stamp => this.playerToken.matches(stamp)); + if (newStamp) { + if (newStamp.value > 1) return OUTCOME.STAMP_2_POINT + return OUTCOME.STAMP_1_POINT + } + return OUTCOME.NONE; + } + containsLoop() { return this.blockQueue.queue.some(block => block instanceof LoopBlock) } @@ -196,12 +216,25 @@ export default class LevelModel { if (this.blockQueue.queue.length === 0) throw new Error(errors.noTransforms) const models = [] let model = this - const steps = this.blockQueue.getStepsFromQueue() + let list = this.blockQueue.getStepsFromQueue(); + const steps = list.transforms; + const stepTypes = list.types; for (let i = 0; i < steps.length && !model.complete; i++) { - model = new LevelModel({ ...model, executionStep: i, step: steps[i], complete: i === steps.length - 1 }) + model = new LevelModel({ ...model, executionStep: i, step: steps[i], stepType: stepTypes[i], complete: i === steps.length - 1 }) models.push(model) } return models } +} + +const OUTCOME = { + NONE: 'NONE', + OUT_OF_BOUNDS: 'OUT_OF_BOUNDS', + MONSTER: 'MONSTER', + STAMP_1_POINT: 'STAMP_1_POINT', + STAMP_2_POINT: 'STAMP_2_POINT', + GEM_YELLOW: 'GEM_YELLOW', + GEM_BLUE: 'GEM_BLUE', + GOAL: 'GOAL' } \ No newline at end of file