{
  "information": {
    "Title": "ejss_model_squaretown Square Town, 2 Player game, first player sets the number of squares, second player use split and merge to form the correct number of squares and submit!",
    "Author": [
      "Shaun Quek",
      "Francisco Esquembre",
      "Loo Kang Wee",
      "F\u00e9lix Jes\u00fas Garc\u00eda Clemente",
      "based on idea by Theresa Heng"
    ],
    "AuthorLogo": [
      "",
      "",
      "",
      "",
      ""
    ],
    "Password": "",
    "Keywords": "",
    "Abstract": "",
    "Copyright": "",
    "Level": "",
    "Language": "",
    "Logo": [
      "./squaretown/Screenshot 2021-06-14 at 10.08.28 AM (2).png"
    ],
    "RunAlways": "true",
    "ModelTab": "",
    "ModelTabTitle": "",
    "ModelName": "",
    "FixedNavigationBar": "false",
    "CSSFile": "",
    "DetectedFiles": [
      "./squaretown/Screenshot 2021-06-14 at 10.08.28 AM (2).png"
    ],
    "AuxiliaryFiles": [
      "./squaretown/"
    ],
    "HTMLHead": "<script async=\"true\" src=\"https://www.googletagmanager.com/gtag/js?id=UA-3326007-19\"></script>\n<script>\n  window.dataLayer = window.dataLayer || [];\n  function gtag(){dataLayer.push(arguments);}\n  gtag('js', new Date());\n  gtag('config', 'UA-3326007-19');\n</script>\n<script data-ad-client=\"ca-pub-0121577198857509\" async=\"true\" src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js\"></script>",
    "SaveInXMLFormat": "false",
    "IncludeSource": "true",
    "IncludeLibrary": "true",
    "UglifyJS": "false",
    "PreviewFullModel": "false",
    "UseInterpreter": "true",
    "UseDeltaForODE": "false"
  },
  "description": {
    "pages": [
      {
        "Name": "Intro Page",
        "Active": "true",
        "Internal": "false",
        "Type": "DESCRIPTION_EDITOR",
        "Locale": "_default_",
        "Title": "Intro Page",
        "External": "false",
        "Code": "<html>\n<head></head>\n<body>\n<p> </p>\n</body>\n</html>"
      }
    ]
  },
  "model": {
    "variables": {
      "pages": [
        {
          "Name": "Var Table",
          "Active": "true",
          "Internal": "false",
          "Type": "VARIABLE_EDITOR",
          "PageComment": "",
          "Variables": [
            {
              "Name": "n",
              "Value": "20",
              "Type": "Object",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "numOfSquares",
              "Value": "n * n",
              "Type": "Object",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "colorDefault",
              "Value": "\"white\"",
              "Type": "String",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "colorSelected",
              "Value": "\"#b3e5fc\"",
              "Type": "String",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "squareX",
              "Value": "0",
              "Type": "Object",
              "Dimension": "numOfSquares",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "squareY",
              "Value": "0",
              "Type": "Object",
              "Dimension": "numOfSquares",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "squareLength",
              "Value": "0",
              "Type": "Object",
              "Dimension": "numOfSquares",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "squareVisible",
              "Value": "false",
              "Type": "Object",
              "Dimension": "numOfSquares",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "squareColor",
              "Value": "colorDefault",
              "Type": "Object",
              "Dimension": "numOfSquares",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "selectedSquares",
              "Value": "{}",
              "Type": "Object",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "availableSquares",
              "Value": "[]",
              "Type": "Object",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "interact",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "font",
              "Value": "\"normal normal clamp(1rem, 2.2vw, 1.35rem)\"",
              "Type": "String",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "instructionFont",
              "Value": "\"normal normal clamp(0.95rem, 1.6vw, 1.1rem)\"",
              "Type": "String",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "totalSquares",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "inputNum",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            },
            {
              "Name": "computerMode",
              "Value": "false",
              "Type": "Object",
              "Dimension": "",
              "Comment": "true when the learner plays against a computer-generated challenge",
              "Domain": "public"
            },
            {
              "Name": "difficulty",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "0 = Easy, 1 = Medium, 2 = Challenge",
              "Domain": "public"
            },
            {
              "Name": "moves",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "number of successful split or merge moves in this round",
              "Domain": "public"
            },
            {
              "Name": "computerPar",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "known number of split moves used to generate the computer target",
              "Domain": "public"
            },
            {
              "Name": "humanScore",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "computer-mode rounds won by the learner",
              "Domain": "public"
            },
            {
              "Name": "computerScore",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "computer-mode rounds won by the computer benchmark",
              "Domain": "public"
            },
            {
              "Name": "roundComplete",
              "Value": "false",
              "Type": "Object",
              "Dimension": "",
              "Comment": "prevents a completed round from being scored twice",
              "Domain": "public"
            },
            {
              "Name": "feedbackText",
              "Value": "\"Choose a mode and set a target to begin.\"",
              "Type": "String",
              "Dimension": "",
              "Comment": "short, persistent feedback shown below the controls",
              "Domain": "public"
            },
            {
              "Name": "moveHistory",
              "Value": "[]",
              "Type": "Object",
              "Dimension": "",
              "Comment": "chronological session log of targets, attempts, moves, submissions, resets, and outcomes",
              "Domain": "public"
            },
            {
              "Name": "roundHistory",
              "Value": "[]",
              "Type": "Object",
              "Dimension": "",
              "Comment": "completed and abandoned round summaries used for trend analysis",
              "Domain": "public"
            },
            {
              "Name": "historySequence",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "session-wide action number",
              "Domain": "public"
            },
            {
              "Name": "roundNumber",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "session-wide round number",
              "Domain": "public"
            },
            {
              "Name": "lastLoggedTarget",
              "Value": "0",
              "Type": "double",
              "Dimension": "",
              "Comment": "confirmed friend-mode target; prevents unrecorded target changes during a round",
              "Domain": "public"
            },
            {
              "Name": "",
              "Value": "",
              "Type": "double",
              "Dimension": "",
              "Comment": "null",
              "Domain": "public"
            }
          ]
        }
      ]
    },
    "initialization": {
      "pages": [
        {
          "Name": "Init Page",
          "Active": "true",
          "Internal": "false",
          "Type": "CODE_EDITOR",
          "Comment": "",
          "Code": "// Start with one 20 by 20 square. The same helper is used by Reset and New Challenge.\nresetBoard(false);\nfeedbackText = \"Friend mode: Player 1 sets a target, then Player 2 builds it.\";\n\n"
        }
      ]
    },
    "evolution": {
      "information": {
        "FPS": "20",
        "SPD": "1",
        "RealTimeVariable": "",
        "Autoplay": "true"
      },
      "pages": []
    },
    "fixed_relations": {
      "pages": []
    },
    "custom": {
      "pages": [
        {
          "Name": "createSquare",
          "Active": "true",
          "Internal": "false",
          "Type": "LIBRARY_EDITOR",
          "Comment": "",
          "Code": "function createSquare(length, x, y) {\n  // if no more squares are available\n  if(availableSquares.length == 0)\n    return;\n  // getting the index of the next available square\n  var idx = availableSquares[0];\n  // setting the position of the square\n  squareX[idx] = x;\n  squareY[idx] = y;\n  squareVisible[idx] = true;\n  squareLength[idx] = length;\n  squareColor[idx] = colorDefault;\n  // remove this index from availableSquares\n  availableSquares.shift();\n  // increment the number of visible squares\n  totalSquares++;\n  return;\n}"
        },
        {
          "Name": "removeSquare",
          "Active": "true",
          "Internal": "false",
          "Type": "LIBRARY_EDITOR",
          "Comment": "",
          "Code": "function removeSquare (idx) {\n  // make the square invisible\n  squareVisible[idx] = false;\n  // add this index to the availableSquares array\n  availableSquares.push(idx);\n  // decrement the total number of squares\n  totalSquares--;\n  return;\n}"
        },
        {
          "Name": "splitSquare",
          "Active": "true",
          "Internal": "false",
          "Type": "LIBRARY_EDITOR",
          "Comment": "",
          "Code": "function splitSquare () {\n  for(var squareIdx in selectedSquares){\n    // make the square unselected\n    delete selectedSquares[squareIdx];\n    squareColor[squareIdx] = colorDefault;\n\n    var curLength = squareLength[squareIdx];\n    // finding the largest factor of the current size\n    var largestFac = 1;\n    for(var j = curLength - 1; j > 1; j--){\n      if(curLength % j == 0){\n        largestFac = j;\n        break;\n      }\n    }\n    var x = squareX[squareIdx], y = squareY[squareIdx];\n    // removing the selected square\n    removeSquare(squareIdx);\n    // creating the split square\n    for(var j = x; j < x + curLength; j += largestFac){\n      for(var k = y; k < y + curLength; k += largestFac){\n        createSquare(largestFac, j, k);\n      }\n    }\n  }\n}"
        },
        {
          "Name": "mergeSquare",
          "Active": "true",
          "Internal": "false",
          "Type": "LIBRARY_EDITOR",
          "Comment": "",
          "Code": "function mergeSquare() {\n  // initialising the grid for the depth-first search later\n  var grid = [], whichIdx = [];\n  for(var i = 0; i < n; i++) {\n    grid[i] = [], whichIdx[i] = [];\n    for(var j = 0; j < n; j++) {\n      grid[i][j] = 0;\n      whichIdx[i][j] = -1;\n    }\n  }\n\n  for(var squareIdx in selectedSquares) {\n    // make the square unselected\n    delete selectedSquares[squareIdx];\n    squareColor[squareIdx] = colorDefault;\n    // filling in the grid\n    var x = squareX[squareIdx], y = squareY[squareIdx];\n    for(var j = y; j < y + squareLength[squareIdx]; j++) {\n      for(var k = x; k < x + squareLength[squareIdx]; k++) {\n        grid[j][k] = 1;\n        whichIdx[j][k] = squareIdx;\n      }\n    }\n  }\n\n  var minX = -1, maxX = -1, minY = -1, maxY = -1, area = 0;\n  var choosenSquares = {};\n  var dfs = function(y, x) {\n    if(grid[y][x] == 0)\n      return;\n    // set the grid square to be occupied\n    grid[y][x] = 0;\n    area++;\n    // choose the square that this grid square belongs to\n    choosenSquares[whichIdx[y][x]] = 1;\n    // setting the min and max x, y values\n    minX = minX == -1 ? x : Math.min(minX, x);\n    minY = minY == -1 ? y : Math.min(minY, y);\n    maxX = Math.max(maxX, x);\n    maxY = Math.max(maxY, y);\n\n    var dy = [0, 0, -1, 1], dx = [1, -1, 0, 0];\n    for(var i = 0; i < 4; i++) {\n      var curX = x + dx[i], curY = y + dy[i];\n      if(curY < 0 || curX < 0 || curY >= n || curX >= n)\n        continue;\n      if(grid[curY][curX] == 1) {\n        dfs(curY, curX);\n      }\n    }\n  };\n  \n  // Search each connected component independently and merge it only when it forms one complete square.\n  for(var i = 0; i < n; i++) {\n    for(var j = 0; j < n; j++) {\n      if(grid[i][j] == 1) {\n        minX = maxX = minY = maxY = -1;\n        area = 0;\n        choosenSquares = {};\n        dfs(i, j);\n        var lengthX = maxX - minX + 1, lengthY = maxY - minY + 1;\n        // if the total area does not form the shape of a square, don't merge\n        if(lengthX != lengthY || lengthX * lengthY != area){\n          continue;\n        }\n        // looping through the chosen squares to remove them\n        for(var chosenIdx in choosenSquares){\n          removeSquare(chosenIdx);\n        }\n        // add the correctly sized square to complete the merge\n        createSquare(lengthX, minX, minY);\n      }  \n    }\n  }\n}"
        },
        {
          "Name": "gameModesAndScaffolds",
          "Active": "true",
          "Internal": "false",
          "Type": "LIBRARY_EDITOR",
          "Comment": "Friend/computer modes, guaranteed-solvable challenges, move benchmark, scoring, and learner scaffolds",
          "Code": "function resetBoard(keepTarget) {\n  selectedSquares = {};\n  availableSquares = [];\n  totalSquares = 0;\n  moves = 0;\n  roundComplete = false;\n\n  for(var i = 0; i < numOfSquares; i++) {\n    squareX[i] = 0;\n    squareY[i] = 0;\n    squareLength[i] = 0;\n    squareVisible[i] = false;\n    squareColor[i] = colorDefault;\n    availableSquares.push(i);\n  }\n\n  createSquare(n, 0, 0);\n  if(!keepTarget) {\n    inputNum = 0;\n    computerPar = 0;\n  }\n}\n\nfunction isValidTarget(value) {\n  return isFinite(value) && Math.floor(value) == value && value >= 1 && value <= numOfSquares;\n}\n\nfunction getDifficultyName() {\n  if(difficulty == 0) return \"Easy\";\n  if(difficulty == 1) return \"Medium\";\n  return \"Challenge\";\n}\n\nfunction newComputerChallenge() {\n  // Every target is constructed from a known legal route:\n  // split the 20-square once (+3), split b of the 10-squares (+3 each),\n  // then split c of the resulting 5-squares (+24 each).\n  var b = 0;\n  var c = 0;\n\n  if(difficulty == 0) {\n    b = Math.floor(Math.random() * 5);\n  }\n  else if(difficulty == 1) {\n    b = 1 + Math.floor(Math.random() * 4);\n    c = Math.floor(Math.random() * (Math.min(4, 4 * b) + 1));\n  }\n  else {\n    b = 1 + Math.floor(Math.random() * 4);\n    c = Math.floor(Math.random() * (4 * b + 1));\n  }\n\n  inputNum = 4 + 3 * b + 24 * c;\n  computerPar = 1 + b + c;\n  resetBoard(true);\n  feedbackText = \"Computer challenge ready. Try to match its \" + computerPar + \"-move benchmark!\";\n}\n\nfunction toggleOpponent() {\n  computerMode = !computerMode;\n  if(computerMode) {\n    newComputerChallenge();\n  }\n  else {\n    resetBoard(false);\n    feedbackText = \"Friend mode: Player 1 sets a target, then Player 2 builds it.\";\n  }\n}\n\nfunction cycleDifficulty() {\n  difficulty = (difficulty + 1) % 3;\n  if(computerMode) {\n    newComputerChallenge();\n  }\n  else {\n    feedbackText = \"Computer difficulty set to \" + getDifficultyName() + \".\";\n  }\n}\n\nfunction getGoalText() {\n  if(computerMode) {\n    return \"GOAL: Make exactly \" + inputNum + \" squares in \" + computerPar + \" moves or fewer.\";\n  }\n  if(isValidTarget(inputNum)) {\n    return \"GOAL: Player 2 makes exactly \" + inputNum + \" squares.\";\n  }\n  return \"GOAL: Player 1 enters a whole-number target from 1 to \" + numOfSquares + \".\";\n}\n\nfunction getProgressText() {\n  var selectedCount = Object.keys(selectedSquares).length;\n  var text = \"Current: \" + totalSquares + \" | Selected: \" + selectedCount + \" | Moves: \" + moves;\n  if(isValidTarget(inputNum)) {\n    text += \" | Target: \" + inputNum;\n  }\n  if(computerMode) {\n    text += \" | Score You \" + humanScore + \" - \" + computerScore + \" Computer\";\n  }\n  return text;\n}\n\nfunction getHintText() {\n  if(roundComplete) {\n    return computerMode ? \"Round complete. Press New Challenge to play again.\" : \"Round complete. Reset for another target.\";\n  }\n  if(!isValidTarget(inputNum)) {\n    return \"Step 1: Enter a target. Step 2: Select square pieces. Step 3: Split or merge, then Submit.\";\n  }\n  if(totalSquares == inputNum) {\n    return \"You reached the target. Press Submit to check your answer!\";\n  }\n  if(Object.keys(selectedSquares).length == 0) {\n    return \"Select a square first. Split makes more pieces; Merge combines touching pieces that form a full square.\";\n  }\n  if(totalSquares < inputNum) {\n    return \"You need \" + (inputNum - totalSquares) + \" more. Splitting selected squares increases the count.\";\n  }\n  return \"You need \" + (totalSquares - inputNum) + \" fewer. Merge selected touching pieces when they form a full square.\";\n}\n\nfunction recordSplit() {\n  if(roundComplete) {\n    feedbackText = \"This round is complete. Start a new round to keep playing.\";\n    return;\n  }\n  var before = totalSquares;\n  splitSquare();\n  if(totalSquares != before) {\n    moves++;\n    feedbackText = \"Split successful: \" + before + \" to \" + totalSquares + \" squares.\";\n  }\n  else {\n    feedbackText = \"That selection cannot be split further. Try a larger square.\";\n  }\n}\n\nfunction recordMerge() {\n  if(roundComplete) {\n    feedbackText = \"This round is complete. Start a new round to keep playing.\";\n    return;\n  }\n  var before = totalSquares;\n  mergeSquare();\n  if(totalSquares != before) {\n    moves++;\n    feedbackText = \"Merge successful: \" + before + \" to \" + totalSquares + \" squares.\";\n  }\n  else {\n    feedbackText = \"Those pieces do not make one complete square yet. Try a different selection.\";\n  }\n}\n\nfunction submitAnswer() {\n  if(!isValidTarget(inputNum)) {\n    feedbackText = \"Enter a whole-number target from 1 to \" + numOfSquares + \".\";\n    _tools.showOkDialog(feedbackText);\n    return;\n  }\n  if(roundComplete) {\n    _tools.showOkDialog(\"This round has already been scored. Start a new round to play again.\");\n    return;\n  }\n\n  if(totalSquares == inputNum) {\n    roundComplete = true;\n    if(computerMode) {\n      if(moves <= computerPar) {\n        humanScore++;\n        feedbackText = \"You win! Exact target in \" + moves + \" moves; computer benchmark: \" + computerPar + \".\";\n      }\n      else {\n        computerScore++;\n        feedbackText = \"Target reached in \" + moves + \" moves. The computer wins this round with \" + computerPar + \".\";\n      }\n    }\n    else {\n      feedbackText = \"Success! Player 2 made exactly \" + inputNum + \" squares in \" + moves + \" moves.\";\n    }\n    _tools.showOkDialog(feedbackText);\n  }\n  else if(totalSquares > inputNum) {\n    feedbackText = \"Too many: merge or reset to remove \" + (totalSquares - inputNum) + \" squares.\";\n    _tools.showOkDialog(feedbackText);\n  }\n  else {\n    feedbackText = \"Not enough yet: add \" + (inputNum - totalSquares) + \" more squares.\";\n    _tools.showOkDialog(feedbackText);\n  }\n}"
        },
        {
          "Name": "moveHistoryAndReflection",
          "Active": "true",
          "Internal": "false",
          "Type": "LIBRARY_EDITOR",
          "Comment": "Running player/action log, round summaries, trend analysis, and reflection prompts",
          "Code": "function getActivePlayerName() {\n  return computerMode ? \"You\" : \"Player 2\";\n}\n\nfunction addHistoryEntry(player, action, beforeCount, afterCount, outcome, detail) {\n  historySequence++;\n  moveHistory.push({\n    sequence: historySequence,\n    round: roundNumber,\n    player: player,\n    action: action,\n    before: beforeCount,\n    after: afterCount,\n    outcome: outcome,\n    detail: detail || \"\",\n    target: inputNum,\n    successfulMoves: moves\n  });\n}\n\nfunction addRoundSummary(result, note) {\n  roundHistory.push({\n    round: roundNumber,\n    mode: computerMode ? \"Computer\" : \"Friend\",\n    target: inputNum,\n    moves: moves,\n    par: computerMode ? computerPar : 0,\n    result: result,\n    note: note || \"\"\n  });\n}\n\nfunction closeOpenRound(reason) {\n  if(roundNumber > 0 && !roundComplete && (moves > 0 || computerMode || lastLoggedTarget > 0)) {\n    addHistoryEntry(\"System\", \"ROUND END\", totalSquares, totalSquares, \"Abandoned\", reason);\n    addRoundSummary(\"Abandoned\", reason);\n  }\n}\n\nfunction startLoggedComputerRound(reason) {\n  roundNumber++;\n  lastLoggedTarget = inputNum;\n  addHistoryEntry(\"Computer\", \"TARGET\", totalSquares, totalSquares, \"Challenge set\", \"Target \" + inputNum + \"; benchmark \" + computerPar + \" moves; \" + getDifficultyName() + \" difficulty; \" + reason);\n}\n\nfunction confirmFriendTarget() {\n  if(!isValidTarget(inputNum)) {\n    feedbackText = \"Player 1: enter a whole-number target from 1 to \" + numOfSquares + \".\";\n    _tools.showOkDialog(feedbackText);\n    return;\n  }\n  closeOpenRound(\"Player 1 set a new target.\");\n  roundNumber++;\n  resetBoard(true);\n  lastLoggedTarget = inputNum;\n  addHistoryEntry(\"Player 1\", \"TARGET\", totalSquares, totalSquares, \"Confirmed\", \"Player 2 must make exactly \" + inputNum + \" squares.\");\n  feedbackText = \"Target \" + inputNum + \" recorded. Pass to Player 2 and begin.\";\n}\n\nfunction isFriendTargetReady() {\n  return computerMode || (isValidTarget(inputNum) && inputNum == lastLoggedTarget && roundNumber > 0);\n}\n\nfunction requireConfirmedTarget() {\n  if(isFriendTargetReady()) return true;\n  feedbackText = \"Player 1 must enter a target and press Set Target before Player 2 moves.\";\n  return false;\n}\n\nfunction trackedSplit() {\n  if(!requireConfirmedTarget()) return;\n  var before = totalSquares;\n  var selectedCount = Object.keys(selectedSquares).length;\n  var movesBefore = moves;\n  recordSplit();\n  var changed = totalSquares != before;\n  addHistoryEntry(getActivePlayerName(), \"SPLIT\", before, totalSquares, changed ? \"Success\" : \"No change\", selectedCount + \" piece(s) selected; successful move count \" + movesBefore + \" -> \" + moves + \".\");\n}\n\nfunction trackedMerge() {\n  if(!requireConfirmedTarget()) return;\n  var before = totalSquares;\n  var selectedCount = Object.keys(selectedSquares).length;\n  var movesBefore = moves;\n  recordMerge();\n  var changed = totalSquares != before;\n  addHistoryEntry(getActivePlayerName(), \"MERGE\", before, totalSquares, changed ? \"Success\" : \"No change\", selectedCount + \" piece(s) selected; successful move count \" + movesBefore + \" -> \" + moves + \".\");\n}\n\nfunction trackedSubmit() {\n  if(!requireConfirmedTarget()) return;\n  var wasComplete = roundComplete;\n  var humanBefore = humanScore;\n  var computerBefore = computerScore;\n  submitAnswer();\n\n  var outcome = \"Already scored\";\n  if(!wasComplete) {\n    if(roundComplete && computerMode && humanScore > humanBefore) outcome = \"You won\";\n    else if(roundComplete && computerMode && computerScore > computerBefore) outcome = \"Computer won\";\n    else if(roundComplete) outcome = \"Target reached\";\n    else if(totalSquares > inputNum) outcome = \"Too many\";\n    else if(totalSquares < inputNum) outcome = \"Not enough\";\n    else outcome = \"Checked\";\n  }\n\n  addHistoryEntry(getActivePlayerName(), \"SUBMIT\", totalSquares, totalSquares, outcome, feedbackText);\n  if(!wasComplete && roundComplete) {\n    addRoundSummary(outcome, feedbackText);\n  }\n}\n\nfunction trackedToggleOpponent() {\n  closeOpenRound(\"Opponent mode changed.\");\n  toggleOpponent();\n  if(computerMode) {\n    startLoggedComputerRound(\"Opponent changed to Computer\");\n  }\n  else {\n    lastLoggedTarget = 0;\n    addHistoryEntry(\"System\", \"MODE\", totalSquares, totalSquares, \"Friend mode\", \"Player 1 sets and confirms the target; Player 2 plays.\");\n  }\n}\n\nfunction trackedCycleDifficulty() {\n  if(computerMode) closeOpenRound(\"Computer difficulty changed.\");\n  cycleDifficulty();\n  if(computerMode) {\n    startLoggedComputerRound(\"Difficulty changed\");\n  }\n  else {\n    addHistoryEntry(\"System\", \"DIFFICULTY\", totalSquares, totalSquares, getDifficultyName(), \"Applies when Computer mode is selected.\");\n  }\n}\n\nfunction trackedResetOrChallenge() {\n  if(computerMode) {\n    closeOpenRound(\"New computer challenge requested.\");\n    newComputerChallenge();\n    startLoggedComputerRound(\"New Challenge button\");\n    return;\n  }\n  if(!requireConfirmedTarget()) return;\n  var before = totalSquares;\n  closeOpenRound(\"Board reset for another attempt at the same target.\");\n  roundNumber++;\n  resetBoard(true);\n  addHistoryEntry(\"Player 2\", \"RESET\", before, totalSquares, \"New attempt\", \"Same target: \" + inputNum + \".\");\n  feedbackText = \"Board reset. Player 2 can try target \" + inputNum + \" again.\";\n}\n\nfunction clearSessionHistory() {\n  moveHistory = [];\n  roundHistory = [];\n  historySequence = 0;\n  addHistoryEntry(\"System\", \"HISTORY\", totalSquares, totalSquares, \"Cleared\", \"A new reflection log starts here; the current board is unchanged.\");\n  feedbackText = \"Session history cleared. The current round and board were kept.\";\n}\n\nfunction getTrackedGoalText() {\n  if(!computerMode && !isFriendTargetReady()) {\n    return \"GOAL: Player 1 enters a target and presses Set Target before handing over to Player 2.\";\n  }\n  return getGoalText();\n}\n\nfunction getTrackedProgressText() {\n  var prefix = roundNumber > 0 ? \"Round \" + roundNumber + \" | \" : \"\";\n  return prefix + getProgressText();\n}\n\nfunction getTrackedHintText() {\n  if(!computerMode && !isFriendTargetReady()) {\n    return \"Player 1: choose a whole-number target, then press Set Target to record the start of the round.\";\n  }\n  return getHintText();\n}\n\nfunction getMoveLogText() {\n  if(moveHistory.length == 0) {\n    return \"No actions recorded yet. Set a target or start a computer challenge.\";\n  }\n  var limit = 20;\n  var start = Math.max(0, moveHistory.length - limit);\n  var lines = [];\n  if(start > 0) lines.push(\"... \" + start + \" earlier action(s) retained in this session\");\n  for(var i = start; i < moveHistory.length; i++) {\n    var entry = moveHistory[i];\n    lines.push(\"#\" + entry.sequence + \" R\" + entry.round + \" | \" + entry.player + \" | \" + entry.action + \" | \" + entry.before + \" -> \" + entry.after + \" | \" + entry.outcome);\n  }\n  return lines.join(\"\\n\");\n}\n\nfunction getRoundSummaryText() {\n  if(roundHistory.length == 0) return \"Round summaries will appear after a round is completed or abandoned.\";\n  var start = Math.max(0, roundHistory.length - 5);\n  var lines = [];\n  for(var i = start; i < roundHistory.length; i++) {\n    var round = roundHistory[i];\n    var benchmark = round.mode == \"Computer\" ? \" / par \" + round.par : \"\";\n    lines.push(\"R\" + round.round + \" \" + round.mode + \": target \" + round.target + \", \" + round.moves + \" moves\" + benchmark + \" - \" + round.result);\n  }\n  return lines.join(\"\\n\");\n}\n\nfunction getTrendText() {\n  var splitSuccess = 0, mergeSuccess = 0, noChange = 0;\n  for(var i = 0; i < moveHistory.length; i++) {\n    var entry = moveHistory[i];\n    if(entry.action == \"SPLIT\" && entry.outcome == \"Success\") splitSuccess++;\n    if(entry.action == \"MERGE\" && entry.outcome == \"Success\") mergeSuccess++;\n    if(entry.outcome == \"No change\") noChange++;\n  }\n\n  var completed = [];\n  var abandoned = 0;\n  for(var j = 0; j < roundHistory.length; j++) {\n    if(roundHistory[j].result == \"Abandoned\") abandoned++;\n    else completed.push(roundHistory[j]);\n  }\n\n  var text = \"Trend: \" + splitSuccess + \" successful split(s), \" + mergeSuccess + \" successful merge(s), \" + noChange + \" no-change attempt(s).\";\n  if(completed.length > 0) {\n    var totalMoves = 0;\n    var bestMoves = completed[0].moves;\n    for(var k = 0; k < completed.length; k++) {\n      totalMoves += completed[k].moves;\n      bestMoves = Math.min(bestMoves, completed[k].moves);\n    }\n    text += \" Completed \" + completed.length + \" round(s); average \" + (Math.round(totalMoves * 10 / completed.length) / 10) + \" moves; best \" + bestMoves + \".\";\n    if(completed.length > 1) {\n      var previous = completed[completed.length - 2].moves;\n      var latest = completed[completed.length - 1].moves;\n      if(latest < previous) text += \" Latest improvement: \" + (previous - latest) + \" fewer move(s).\";\n      else if(latest > previous) text += \" Latest round used \" + (latest - previous) + \" more move(s).\";\n      else text += \" Latest two rounds used the same number of moves.\";\n    }\n  }\n  if(abandoned > 0) text += \" Abandoned rounds: \" + abandoned + \".\";\n  return text;\n}\n\nfunction getReflectionText() {\n  if(moveHistory.length == 0) return \"Reflection: What do you predict the first split will do to the square count?\";\n  var last = moveHistory[moveHistory.length - 1];\n  if(last.outcome == \"No change\") return \"Reflection: Why did that selection make no change? What complete square or larger piece is needed?\";\n  if(last.action == \"SUBMIT\" && (last.outcome == \"You won\" || last.outcome == \"Target reached\")) return \"Reflection: Which move changed the count most, and could you reach the same target with fewer moves?\";\n  if(last.action == \"SUBMIT\" && last.outcome == \"Computer won\") return \"Reflection: Compare your sequence with the computer benchmark. Which move could be removed or replaced?\";\n  if(last.action == \"SPLIT\" || last.action == \"MERGE\") return \"Reflection: Before the next move, predict whether the count will rise or fall and by how much.\";\n  return \"Reflection: Review the last actions. What pattern seems to lead toward the target?\";\n}"
        },
        {
          "Name": "friendTargetHistoryAccuracy",
          "Active": "true",
          "Internal": "false",
          "Type": "LIBRARY_EDITOR",
          "Comment": "Preserves the previous confirmed target when Player 1 edits the input before starting a new round",
          "Code": "function confirmFriendTargetSafely() {\n  var newTarget = inputNum;\n  if(!isValidTarget(newTarget)) {\n    feedbackText = \"Player 1: enter a whole-number target from 1 to \" + numOfSquares + \".\";\n    _tools.showOkDialog(feedbackText);\n    return;\n  }\n\n  if(roundNumber > 0 && !roundComplete && lastLoggedTarget > 0) {\n    inputNum = lastLoggedTarget;\n    closeOpenRound(\"Player 1 set a new target.\");\n    inputNum = newTarget;\n  }\n\n  roundNumber++;\n  resetBoard(true);\n  lastLoggedTarget = inputNum;\n  addHistoryEntry(\"Player 1\", \"TARGET\", totalSquares, totalSquares, \"Confirmed\", \"Player 2 must make exactly \" + inputNum + \" squares.\");\n  feedbackText = \"Target \" + inputNum + \" recorded. Pass to Player 2 and begin.\";\n}"
        },
        {
          "Name": "exactTargetRoundFinalization",
          "Active": "true",
          "Internal": "false",
          "Type": "LIBRARY_EDITOR",
          "Comment": "Counts an exact target as completed before the next round, even when Submit was not pressed",
          "Code": "function finalizeRoundAccurately(reason) {\n  if(roundNumber <= 0 || roundComplete) return false;\n\n  if(isValidTarget(inputNum) && totalSquares == inputNum) {\n    var outcome = \"Target reached\";\n    if(computerMode) {\n      if(moves <= computerPar) {\n        humanScore++;\n        outcome = \"You won\";\n        feedbackText = \"Round completed automatically at the exact target in \" + moves + \" moves; computer benchmark: \" + computerPar + \".\";\n      }\n      else {\n        computerScore++;\n        outcome = \"Computer won\";\n        feedbackText = \"Exact target reached in \" + moves + \" moves; computer benchmark: \" + computerPar + \".\";\n      }\n    }\n    else {\n      feedbackText = \"Round completed automatically: Player 2 made exactly \" + inputNum + \" squares in \" + moves + \" moves.\";\n    }\n\n    roundComplete = true;\n    addHistoryEntry(getActivePlayerName(), \"ROUND COMPLETE\", totalSquares, totalSquares, outcome, feedbackText + \" \" + reason);\n    addRoundSummary(outcome, feedbackText);\n    return true;\n  }\n\n  if(moves > 0 || computerMode || lastLoggedTarget > 0) {\n    addHistoryEntry(\"System\", \"ROUND END\", totalSquares, totalSquares, \"Abandoned\", reason);\n    addRoundSummary(\"Abandoned\", reason);\n    return false;\n  }\n  return false;\n}\n\nfunction trackedToggleOpponentAccurately() {\n  finalizeRoundAccurately(\"Opponent mode changed.\");\n  toggleOpponent();\n  if(computerMode) {\n    startLoggedComputerRound(\"Opponent changed to Computer\");\n  }\n  else {\n    lastLoggedTarget = 0;\n    addHistoryEntry(\"System\", \"MODE\", totalSquares, totalSquares, \"Friend mode\", \"Player 1 sets and confirms the target; Player 2 plays.\");\n  }\n}\n\nfunction trackedCycleDifficultyAccurately() {\n  if(computerMode) finalizeRoundAccurately(\"Computer difficulty changed.\");\n  cycleDifficulty();\n  if(computerMode) {\n    startLoggedComputerRound(\"Difficulty changed\");\n  }\n  else {\n    addHistoryEntry(\"System\", \"DIFFICULTY\", totalSquares, totalSquares, getDifficultyName(), \"Applies when Computer mode is selected.\");\n  }\n}\n\nfunction trackedResetOrChallengeAccurately() {\n  if(computerMode) {\n    finalizeRoundAccurately(\"New computer challenge requested.\");\n    newComputerChallenge();\n    startLoggedComputerRound(\"New Challenge button\");\n    return;\n  }\n  if(!requireConfirmedTarget()) return;\n  var before = totalSquares;\n  finalizeRoundAccurately(\"Board reset for another attempt at the same target.\");\n  roundNumber++;\n  resetBoard(true);\n  addHistoryEntry(\"Player 2\", \"RESET\", before, totalSquares, \"New attempt\", \"Same target: \" + inputNum + \".\");\n  feedbackText = \"Board reset. Player 2 can try target \" + inputNum + \" again.\";\n}\n\nfunction confirmFriendTargetAccurately() {\n  var newTarget = inputNum;\n  if(!isValidTarget(newTarget)) {\n    feedbackText = \"Player 1: enter a whole-number target from 1 to \" + numOfSquares + \".\";\n    _tools.showOkDialog(feedbackText);\n    return;\n  }\n\n  if(roundNumber > 0 && !roundComplete && lastLoggedTarget > 0) {\n    inputNum = lastLoggedTarget;\n    finalizeRoundAccurately(\"Player 1 set a new target.\");\n    inputNum = newTarget;\n  }\n\n  roundNumber++;\n  resetBoard(true);\n  lastLoggedTarget = inputNum;\n  addHistoryEntry(\"Player 1\", \"TARGET\", totalSquares, totalSquares, \"Confirmed\", \"Player 2 must make exactly \" + inputNum + \" squares.\");\n  feedbackText = \"Target \" + inputNum + \" recorded. Pass to Player 2 and begin.\";\n}\n\nfunction getReflectionTextAccurately() {\n  if(moveHistory.length > 0) {\n    var last = moveHistory[moveHistory.length - 1];\n    if(last.action == \"ROUND COMPLETE\") {\n      if(last.outcome == \"Computer won\") return \"Reflection: The target was correct, but compare your sequence with the computer benchmark. Which move could be removed or replaced?\";\n      return \"Reflection: The exact target was reached. Which move changed the count most, and could the same target be reached another way?\";\n    }\n  }\n  return getReflectionText();\n}"
        },
        {
          "Name": "practiceModeBoardActions",
          "Active": "true",
          "Internal": "false",
          "Type": "LIBRARY_EDITOR",
          "Comment": "Keeps Split and Merge available before a target is confirmed; Submit still requires a target",
          "Code": "function getBoardActionPlayerName() {\n  if(computerMode) return \"You\";\n  if(isFriendTargetReady()) return \"Player 2\";\n  return \"Practice\";\n}\n\nfunction trackedSplitAlwaysAvailable() {\n  var targetReady = isFriendTargetReady();\n  var before = totalSquares;\n  var selectedCount = Object.keys(selectedSquares).length;\n  var movesBefore = moves;\n  recordSplit();\n  var changed = totalSquares != before;\n  addHistoryEntry(getBoardActionPlayerName(), \"SPLIT\", before, totalSquares, changed ? \"Success\" : \"No change\", selectedCount + \" piece(s) selected; successful move count \" + movesBefore + \" -> \" + moves + \".\");\n\n  if(!targetReady && changed) {\n    feedbackText = \"Practice split successful: \" + before + \" to \" + totalSquares + \" squares. Set a target when you are ready to start a scored round.\";\n  }\n  else if(!targetReady && !changed) {\n    feedbackText = \"Practice move made no change. Select a larger square, or set a target when ready.\";\n  }\n}\n\nfunction trackedMergeAlwaysAvailable() {\n  var targetReady = isFriendTargetReady();\n  var before = totalSquares;\n  var selectedCount = Object.keys(selectedSquares).length;\n  var movesBefore = moves;\n  recordMerge();\n  var changed = totalSquares != before;\n  addHistoryEntry(getBoardActionPlayerName(), \"MERGE\", before, totalSquares, changed ? \"Success\" : \"No change\", selectedCount + \" piece(s) selected; successful move count \" + movesBefore + \" -> \" + moves + \".\");\n\n  if(!targetReady && changed) {\n    feedbackText = \"Practice merge successful: \" + before + \" to \" + totalSquares + \" squares. Set a target when you are ready to start a scored round.\";\n  }\n  else if(!targetReady && !changed) {\n    feedbackText = \"Practice move made no change. Select touching pieces that form a complete square, or set a target when ready.\";\n  }\n}"
        },
        {
          "Name": "toggleFullScreen",
          "Active": "true",
          "Internal": "false",
          "Type": "LIBRARY_EDITOR",
          "Comment": "",
          "Code": "//https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode\n// does not work for iOS \n/*jslint browser:true */\nfunction toggleFullScreen() {\n  if (!document.fullscreenElement &&    // alternative standard method\n      !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) {  // current working methods\n    if (document.documentElement.requestFullscreen) {\n      document.documentElement.requestFullscreen();\n    } else if (document.documentElement.msRequestFullscreen) {\n      document.documentElement.msRequestFullscreen();\n    } else if (document.documentElement.mozRequestFullScreen) {\n      document.documentElement.mozRequestFullScreen();\n    } else if (document.documentElement.webkitRequestFullscreen) {\n      document.documentElement.webkitRequestFullscreen();\n    }\n  } else {\n    if (document.exitFullscreen) {\n      document.exitFullscreen();\n    } else if (document.msExitFullscreen) {\n      document.msExitFullscreen();\n    } else if (document.mozCancelFullScreen) {\n      document.mozCancelFullScreen();\n    } else if (document.webkitExitFullscreen) {\n      document.webkitExitFullscreen();\n    }\n  }\n}"
        }
      ]
    },
    "elements": {
      "list": []
    }
  },
  "view": {
    "Tree": [
      {
        "Name": "gridContainer",
        "Type": "Panel",
        "Expanded": "true",
        "Properties": [
          {
            "name": "Display",
            "value": "\"grid\""
          },
          {
            "name": "Width",
            "value": "\"100%\""
          },
          {
            "name": "CSS",
            "value": "{\n  \"grid-template-columns\": \"repeat(auto-fit, minmax(min(100%, 420px), 1fr))\",\n  \"align-items\": \"start\",\n  \"gap\": \"clamp(0.75rem, 2vw, 1.25rem)\",\n  \"width\": \"100%\",\n  \"max-width\": \"1200px\",\n  \"margin\": \"0 auto\",\n  \"padding\": \"clamp(0.5rem, 2vw, 1rem)\",\n  \"box-sizing\": \"border-box\"\n}"
          },
          {
            "name": "Height",
            "value": "\"90vh\""
          }
        ],
        "Children": [
          {
            "Name": "leftPanel",
            "Type": "Panel",
            "Expanded": "true",
            "Properties": [
              {
                "name": "Background",
                "value": "\"Green\""
              },
              {
                "name": "Width",
                "value": "\"100%\""
              }
            ],
            "Children": [
              {
                "Name": "plottingPanel",
                "Type": "PlottingPanel",
                "Expanded": "true",
                "Properties": [
                  {
                    "name": "Gutters",
                    "value": "[0, 0, 0, 0]"
                  },
                  {
                    "name": "Enabled",
                    "value": "true"
                  },
                  {
                    "name": "OnDoubleClick",
                    "value": "toggleFullScreen();"
                  },
                  {
                    "name": "SquareAspect",
                    "value": "true"
                  },
                  {
                    "name": "MaximumY",
                    "value": "n"
                  },
                  {
                    "name": "MaximumX",
                    "value": "n"
                  },
                  {
                    "name": "MinimumX",
                    "value": "0"
                  },
                  {
                    "name": "MinimumY",
                    "value": "0"
                  },
                  {
                    "name": "AutoScaleY",
                    "value": "true"
                  },
                  {
                    "name": "AutoScaleX",
                    "value": "true"
                  },
                  {
                    "name": "CursorTypeForMove",
                    "value": "\"pointer\""
                  },
                  {
                    "name": "CSS",
                    "value": "{\n  \"width\": \"100%\",\n  \"height\": \"100%\",\n  \"min-width\": \"0\",\n  \"min-height\": \"0\",\n  \"touch-action\": \"manipulation\"\n}\n"
                  },
                  {
                    "name": "XFixedTick",
                    "value": "0"
                  },
                  {
                    "name": "YFixedTick",
                    "value": "0"
                  },
                  {
                    "name": "XTickStep",
                    "value": "20"
                  },
                  {
                    "name": "YTickStep",
                    "value": "20"
                  },
                  {
                    "name": "XAutoTicks",
                    "value": "false"
                  },
                  {
                    "name": "YAutoTicks",
                    "value": "false"
                  },
                  {
                    "name": "AxisXShow",
                    "value": "false"
                  },
                  {
                    "name": "AxisYShow",
                    "value": "false"
                  },
                  {
                    "name": "GridXShow",
                    "value": "false"
                  },
                  {
                    "name": "GridYShow",
                    "value": "false"
                  },
                  {
                    "name": "Width",
                    "value": "\"50%\""
                  }
                ],
                "Children": [
                  {
                    "Name": "shapeSet",
                    "Type": "ShapeSet2D",
                    "Properties": [
                      {
                        "name": "FillColor",
                        "value": "squareColor"
                      },
                      {
                        "name": "RelativePosition",
                        "value": "\"SOUTH_WEST\""
                      },
                      {
                        "name": "ShapeType",
                        "value": "\"RECTANGLE\""
                      },
                      {
                        "name": "OnDoubleClick",
                        "value": "toggleFullScreen();"
                      },
                      {
                        "name": "ElementInteracted",
                        "value": "interact"
                      },
                      {
                        "name": "NumberOfElements",
                        "value": "n * n"
                      },
                      {
                        "name": "Sensitivity",
                        "value": "0"
                      },
                      {
                        "name": "SizeX",
                        "value": "squareLength"
                      },
                      {
                        "name": "X",
                        "value": "squareX"
                      },
                      {
                        "name": "Y",
                        "value": "squareY"
                      },
                      {
                        "name": "Visibility",
                        "value": "squareVisible"
                      },
                      {
                        "name": "OnPress",
                        "value": "if(selectedSquares.hasOwnProperty(interact)){\n  delete selectedSquares[interact];\n  squareColor[interact] = colorDefault;\n}\nelse{\n  selectedSquares[interact] = 1;\n  squareColor[interact] = colorSelected;\n}\n\n"
                      },
                      {
                        "name": "SizeY",
                        "value": "squareLength"
                      },
                      {
                        "name": "LineWidth",
                        "value": "2"
                      },
                      {
                        "name": "EnabledPosition",
                        "value": "\"ENABLED_NO_MOVE\""
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "Name": "rightPanel",
            "Type": "Panel",
            "Expanded": "true",
            "Properties": [
              {
                "name": "CSS",
                "value": "{\n  \"flex-direction\": \"column\",\n  \"justify-content\": \"flex-start\",\n  \"gap\": \"clamp(0.65rem, 2vw, 0.9rem)\",\n  \"width\": \"100%\",\n  \"min-width\": \"0\",\n  \"padding\": \"clamp(0.25rem, 1.5vw, 0.75rem)\",\n  \"box-sizing\": \"border-box\",\n  \"overflow-wrap\": \"anywhere\"\n}\n"
              },
              {
                "name": "Display",
                "value": "\"flex\""
              },
              {
                "name": "Width",
                "value": "\"50%\""
              }
            ],
            "Children": [
              {
                "Name": "modeContainer",
                "Type": "Panel",
                "Expanded": "true",
                "Properties": [
                  {
                    "name": "CSS",
                    "value": "{\n  \"display\": \"grid\",\n  \"grid-template-columns\": \"repeat(auto-fit, minmax(min(100%, 150px), 1fr))\",\n  \"gap\": \"0.5rem\",\n  \"width\": \"100%\"\n}\n"
                  }
                ],
                "Children": [
                  {
                    "Name": "opponentBtn",
                    "Type": "Button",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "computerMode ? \"Opponent: Computer\" : \"Opponent: Friend\""
                      },
                      {
                        "name": "OnClick",
                        "value": "trackedToggleOpponentAccurately();"
                      },
                      {
                        "name": "Background",
                        "value": "computerMode ? \"#80cbc4\" : \"#b3e5fc\""
                      },
                      {
                        "name": "Font",
                        "value": "instructionFont"
                      },
                      {
                        "name": "CSS",
                        "value": "{\n  \"width\": \"100%\",\n  \"padding\": \"0.75em\",\n  \"min-height\": \"48px\",\n  \"font-weight\": \"bold\",\n  \"box-sizing\": \"border-box\"\n}\n"
                      }
                    ]
                  },
                  {
                    "Name": "difficultyBtn",
                    "Type": "Button",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "\"Difficulty: \" + getDifficultyName()"
                      },
                      {
                        "name": "OnClick",
                        "value": "trackedCycleDifficultyAccurately();"
                      },
                      {
                        "name": "Background",
                        "value": "\"#d1c4e9\""
                      },
                      {
                        "name": "Font",
                        "value": "instructionFont"
                      },
                      {
                        "name": "CSS",
                        "value": "{\n  \"width\": \"100%\",\n  \"padding\": \"0.75em\",\n  \"min-height\": \"48px\",\n  \"font-weight\": \"bold\",\n  \"box-sizing\": \"border-box\"\n}\n"
                      }
                    ]
                  },
                  {
                    "Name": "inputContainer",
                    "Type": "Panel",
                    "Expanded": "false",
                    "Properties": [
                      {
                        "name": "CSS",
                        "value": "{\n  \"display\": \"grid\",\n  \"grid-template-columns\": \"minmax(0, 1fr) minmax(5.5rem, 7rem)\",\n  \"align-items\": \"center\",\n  \"gap\": \"0.5rem\",\n  \"width\": \"100%\"\n}\n"
                      }
                    ],
                    "Children": [
                      {
                        "Name": "label",
                        "Type": "Label",
                        "Properties": [
                          {
                            "name": "Text",
                            "value": "computerMode ? \"Computer target: \" : \"Player 1 target (1-400): \""
                          },
                          {
                            "name": "Font",
                            "value": "font"
                          },
                          {
                            "name": "CSS",
                            "value": "{\n  \"min-width\": \"0\",\n  \"line-height\": \"1.3\",\n  \"text-align\": \"left\"\n}\n"
                          }
                        ]
                      },
                      {
                        "Name": "inputVal",
                        "Type": "ParsedField",
                        "Properties": [
                          {
                            "name": "Format",
                            "value": "\"0\""
                          },
                          {
                            "name": "Value",
                            "value": "inputNum"
                          },
                          {
                            "name": "Font",
                            "value": "font"
                          },
                          {
                            "name": "Enabled",
                            "value": "!computerMode"
                          },
                          {
                            "name": "CSS",
                            "value": "{\n  \"width\": \"100%\",\n  \"max-width\": \"none\",\n  \"min-width\": \"0\",\n  \"min-height\": \"48px\",\n  \"text-align\": \"center\",\n  \"box-sizing\": \"border-box\"\n}\n"
                          }
                        ]
                      },
                      {
                        "Name": "setTargetBtn",
                        "Type": "Button",
                        "Properties": [
                          {
                            "name": "Text",
                            "value": "\"Set Target\""
                          },
                          {
                            "name": "OnClick",
                            "value": "confirmFriendTargetAccurately();"
                          },
                          {
                            "name": "Enabled",
                            "value": "!computerMode"
                          },
                          {
                            "name": "Background",
                            "value": "\"#c5e1a5\""
                          },
                          {
                            "name": "Font",
                            "value": "instructionFont"
                          },
                          {
                            "name": "CSS",
                            "value": "{\n  \"grid-column\": \"1 / -1\",\n  \"width\": \"100%\",\n  \"padding\": \"0.7em\",\n  \"min-height\": \"48px\",\n  \"font-weight\": \"bold\",\n  \"box-sizing\": \"border-box\"\n}\n"
                          }
                        ]
                      }
                    ]
                  }
                ]
              },
              {
                "Name": "goalContainer",
                "Type": "Panel",
                "Expanded": "true",
                "Properties": [
                  {
                    "name": "CSS",
                    "value": "{\n  \"background\": \"#fffde7\",\n  \"border\": \"2px solid #f9a825\",\n  \"border-radius\": \"10px\",\n  \"padding\": \"clamp(0.65rem, 2vw, 0.9rem)\",\n  \"box-sizing\": \"border-box\",\n  \"width\": \"100%\",\n  \"line-height\": \"1.4\"\n}\n"
                  }
                ],
                "Children": [
                  {
                    "Name": "goalLabel",
                    "Type": "Label",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "getTrackedGoalText()"
                      },
                      {
                        "name": "Font",
                        "value": "\"bold clamp(1rem, 2.5vw, 1.2rem) sans-serif\""
                      },
                      {
                        "name": "TextAlign",
                        "value": "\"center\""
                      },
                      {
                        "name": "Display",
                        "value": "\"block\""
                      }
                    ]
                  },
                  {
                    "Name": "progressLabel",
                    "Type": "Label",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "getTrackedProgressText()"
                      },
                      {
                        "name": "Font",
                        "value": "instructionFont"
                      },
                      {
                        "name": "TextAlign",
                        "value": "\"center\""
                      },
                      {
                        "name": "Display",
                        "value": "\"block\""
                      },
                      {
                        "name": "CSS",
                        "value": "{\n  \"margin-top\": \"0.5rem\",\n  \"line-height\": \"1.4\"\n}\n"
                      }
                    ]
                  },
                  {
                    "Name": "hintLabel",
                    "Type": "Label",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "getTrackedHintText()"
                      },
                      {
                        "name": "Font",
                        "value": "instructionFont"
                      },
                      {
                        "name": "TextAlign",
                        "value": "\"center\""
                      },
                      {
                        "name": "Display",
                        "value": "\"block\""
                      },
                      {
                        "name": "CSS",
                        "value": "{\n  \"margin-top\": \"0.5rem\",\n  \"color\": \"#37474f\",\n  \"line-height\": \"1.4\"\n}\n"
                      }
                    ]
                  },
                  {
                    "Name": "splitMergeContainer",
                    "Type": "Panel",
                    "Expanded": "false",
                    "Properties": [
                      {
                        "name": "Width",
                        "value": "\"100%\""
                      },
                      {
                        "name": "CSS",
                        "value": "{\n  \"display\": \"grid\",\n  \"grid-template-columns\": \"1fr 1fr\",\n  \"gap\": \"0.5rem\",\n  \"width\": \"100%\",\n  \"align-items\": \"stretch\"\n}\n"
                      }
                    ],
                    "Children": [
                      {
                        "Name": "instructions",
                        "Type": "Label",
                        "Properties": [
                          {
                            "name": "CSS",
                            "value": "{\n  \"grid-column\": \"1 / -1\",\n  \"margin\": \"0\",\n  \"padding\": \"0.25rem\",\n  \"white-space\": \"pre-line\",\n  \"line-height\": \"1.4\"\n}\n"
                          },
                          {
                            "name": "TextAlign",
                            "value": "\"center\""
                          },
                          {
                            "name": "Text",
                            "value": "\"1. Select pieces on the board.\\n2. Split to make more or Merge to make fewer.\\n3. Submit when Current equals Target.\""
                          },
                          {
                            "name": "Width",
                            "value": "\"100%\""
                          },
                          {
                            "name": "Font",
                            "value": "instructionFont"
                          },
                          {
                            "name": "Display",
                            "value": "\"block\""
                          }
                        ]
                      },
                      {
                        "Name": "splitBtn",
                        "Type": "Button",
                        "Properties": [
                          {
                            "name": "CSS",
                            "value": "{\n  \"width\": \"100%\",\n  \"padding\": \"0.75em\",\n  \"min-height\": \"48px\",\n  \"font-weight\": \"bold\",\n  \"box-sizing\": \"border-box\"\n}\n"
                          },
                          {
                            "name": "Background",
                            "value": "\"#81d4fa\""
                          },
                          {
                            "name": "Text",
                            "value": "\"Split\""
                          },
                          {
                            "name": "OnClick",
                            "value": "trackedSplitAlwaysAvailable();"
                          },
                          {
                            "name": "Width",
                            "value": "\"100%\""
                          },
                          {
                            "name": "Font",
                            "value": "font"
                          }
                        ]
                      },
                      {
                        "Name": "mergeBtn",
                        "Type": "Button",
                        "Properties": [
                          {
                            "name": "CSS",
                            "value": "{\n  \"width\": \"100%\",\n  \"padding\": \"0.75em\",\n  \"min-height\": \"48px\",\n  \"font-weight\": \"bold\",\n  \"box-sizing\": \"border-box\"\n}\n"
                          },
                          {
                            "name": "Background",
                            "value": "\"#ef9a9a\""
                          },
                          {
                            "name": "Text",
                            "value": "\"Merge\""
                          },
                          {
                            "name": "OnClick",
                            "value": "trackedMergeAlwaysAvailable();"
                          },
                          {
                            "name": "Width",
                            "value": "\"100%\""
                          },
                          {
                            "name": "Font",
                            "value": "font"
                          }
                        ]
                      }
                    ]
                  }
                ]
              },
              {
                "Name": "feedbackLabel",
                "Type": "Label",
                "Properties": [
                  {
                    "name": "Text",
                    "value": "feedbackText"
                  },
                  {
                    "name": "Font",
                    "value": "instructionFont"
                  },
                  {
                    "name": "TextAlign",
                    "value": "\"center\""
                  },
                  {
                    "name": "Display",
                    "value": "\"block\""
                  },
                  {
                    "name": "CSS",
                    "value": "{\n  \"background\": \"#eceff1\",\n  \"border-radius\": \"8px\",\n  \"padding\": \"0.7rem\",\n  \"box-sizing\": \"border-box\",\n  \"width\": \"100%\",\n  \"line-height\": \"1.4\"\n}\n"
                  }
                ]
              },
              {
                "Name": "historyReflectionPanel",
                "Type": "Panel",
                "Expanded": "true",
                "Properties": [
                  {
                    "name": "CSS",
                    "value": "{\n  \"background\": \"#f5f7fa\",\n  \"border\": \"2px solid #78909c\",\n  \"border-radius\": \"10px\",\n  \"padding\": \"clamp(0.65rem, 2vw, 0.9rem)\",\n  \"box-sizing\": \"border-box\",\n  \"width\": \"100%\",\n  \"min-width\": \"0\",\n  \"order\": \"2\"\n}\n"
                  }
                ],
                "Children": [
                  {
                    "Name": "historyTitle",
                    "Type": "Label",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "\"Move History and Reflection (\" + moveHistory.length + \" actions)\""
                      },
                      {
                        "name": "Font",
                        "value": "\"bold clamp(1rem, 2.5vw, 1.15rem) sans-serif\""
                      },
                      {
                        "name": "TextAlign",
                        "value": "\"center\""
                      },
                      {
                        "name": "Display",
                        "value": "\"block\""
                      }
                    ]
                  },
                  {
                    "Name": "trendLabel",
                    "Type": "Label",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "getTrendText()"
                      },
                      {
                        "name": "Font",
                        "value": "instructionFont"
                      },
                      {
                        "name": "Display",
                        "value": "\"block\""
                      },
                      {
                        "name": "TextAlign",
                        "value": "\"left\""
                      },
                      {
                        "name": "CSS",
                        "value": "{\n  \"margin-top\": \"0.5rem\",\n  \"padding\": \"0.55rem\",\n  \"background\": \"#e8f5e9\",\n  \"border-radius\": \"6px\",\n  \"line-height\": \"1.4\"\n}\n"
                      }
                    ]
                  },
                  {
                    "Name": "roundSummaryLabel",
                    "Type": "Label",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "getRoundSummaryText()"
                      },
                      {
                        "name": "Font",
                        "value": "\"normal clamp(0.88rem, 2.4vw, 1rem) monospace\""
                      },
                      {
                        "name": "Display",
                        "value": "\"block\""
                      },
                      {
                        "name": "TextAlign",
                        "value": "\"left\""
                      },
                      {
                        "name": "CSS",
                        "value": "{\n  \"white-space\": \"pre-wrap\",\n  \"overflow-wrap\": \"anywhere\",\n  \"margin-top\": \"0.5rem\",\n  \"padding\": \"0.55rem\",\n  \"background\": \"#fffde7\",\n  \"border-radius\": \"6px\",\n  \"line-height\": \"1.4\"\n}\n"
                      }
                    ]
                  },
                  {
                    "Name": "moveLogLabel",
                    "Type": "Label",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "getMoveLogText()"
                      },
                      {
                        "name": "Font",
                        "value": "\"normal clamp(0.86rem, 2.3vw, 0.98rem) monospace\""
                      },
                      {
                        "name": "Display",
                        "value": "\"block\""
                      },
                      {
                        "name": "TextAlign",
                        "value": "\"left\""
                      },
                      {
                        "name": "CSS",
                        "value": "{\n  \"white-space\": \"pre-wrap\",\n  \"overflow-wrap\": \"anywhere\",\n  \"max-height\": \"220px\",\n  \"overflow-y\": \"auto\",\n  \"margin-top\": \"0.5rem\",\n  \"padding\": \"0.55rem\",\n  \"background\": \"#ffffff\",\n  \"border\": \"1px solid #cfd8dc\",\n  \"border-radius\": \"6px\",\n  \"line-height\": \"1.45\"\n}\n"
                      }
                    ]
                  },
                  {
                    "Name": "reflectionLabel",
                    "Type": "Label",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "getReflectionTextAccurately()"
                      },
                      {
                        "name": "Font",
                        "value": "instructionFont"
                      },
                      {
                        "name": "Display",
                        "value": "\"block\""
                      },
                      {
                        "name": "TextAlign",
                        "value": "\"left\""
                      },
                      {
                        "name": "CSS",
                        "value": "{\n  \"margin-top\": \"0.5rem\",\n  \"padding\": \"0.65rem\",\n  \"background\": \"#e3f2fd\",\n  \"border-left\": \"4px solid #1e88e5\",\n  \"border-radius\": \"4px\",\n  \"line-height\": \"1.4\"\n}\n"
                      }
                    ]
                  },
                  {
                    "Name": "clearHistoryBtn",
                    "Type": "Button",
                    "Properties": [
                      {
                        "name": "Text",
                        "value": "\"Clear Session Log\""
                      },
                      {
                        "name": "OnClick",
                        "value": "clearSessionHistory();"
                      },
                      {
                        "name": "Background",
                        "value": "\"#cfd8dc\""
                      },
                      {
                        "name": "Font",
                        "value": "instructionFont"
                      },
                      {
                        "name": "CSS",
                        "value": "{\n  \"width\": \"100%\",\n  \"margin-top\": \"0.5rem\",\n  \"padding\": \"0.7em\",\n  \"min-height\": \"48px\",\n  \"box-sizing\": \"border-box\"\n}\n"
                      }
                    ]
                  }
                ]
              },
              {
                "Name": "submitResetContainer",
                "Type": "Panel",
                "Expanded": "true",
                "Properties": [
                  {
                    "name": "Width",
                    "value": "\"100%\""
                  },
                  {
                    "name": "Display",
                    "value": "\"inline-flex\""
                  }
                ],
                "Children": [
                  {
                    "Name": "SubmitBtn",
                    "Type": "Button",
                    "Properties": [
                      {
                        "name": "CSS",
                        "value": "{\n  \"width\": \"100%\",\n  \"padding\": \"0.8em\",\n  \"min-height\": \"50px\",\n  \"font-weight\": \"bold\",\n  \"box-sizing\": \"border-box\"\n}\n"
                      },
                      {
                        "name": "Background",
                        "value": "\"#fff176\""
                      },
                      {
                        "name": "Text",
                        "value": "\"Submit\""
                      },
                      {
                        "name": "Width",
                        "value": "\"100%\""
                      },
                      {
                        "name": "OnClick",
                        "value": "trackedSubmit();"
                      },
                      {
                        "name": "Font",
                        "value": "font"
                      }
                    ]
                  },
                  {
                    "Name": "resetBtn",
                    "Type": "Button",
                    "Properties": [
                      {
                        "name": "CSS",
                        "value": "{\n  \"width\": \"100%\",\n  \"padding\": \"0.8em\",\n  \"min-height\": \"50px\",\n  \"font-weight\": \"bold\",\n  \"box-sizing\": \"border-box\"\n}\n"
                      },
                      {
                        "name": "Background",
                        "value": "\"#b0bec5\""
                      },
                      {
                        "name": "Text",
                        "value": "computerMode ? \"New Challenge\" : \"Reset Board\""
                      },
                      {
                        "name": "OnClick",
                        "value": "trackedResetOrChallengeAccurately();"
                      },
                      {
                        "name": "Width",
                        "value": "\"100%\""
                      },
                      {
                        "name": "Font",
                        "value": "font"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "Name": "html",
            "Type": "Panel",
            "Properties": [
              {
                "name": "Html",
                "value": "<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/jLnULdiO014?si=iS-d4tzR5w88Rzjn\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe><article class=\"square-town-guide\">\n  <h2>Description</h2>\n  <p>\n    <strong>Square Town</strong> is an interactive mathematics game where students are tasked with splitting and merging squares within a grid to match a specified target number. The game fits within a 20&times;20 grid, allowing for up to 400 total squares.\n  </p>\n  <p>\n    To play, students select squares on the grid (highlighted in blue) and click the <strong>Split</strong> or <strong>Merge</strong> buttons to adjust the grid arrangement. Once the target number of squares is formed, pressing <strong>Submit</strong> checks the answer.\n  </p>\n\n  <h2>Suggested Ways to Use \"Square Town\" in the Classroom</h2>\n  \n  <section>\n    <h3>1. Interactive Two-Player Cooperative Game</h3>\n    <ul>\n      <li><strong>Partner Challenge:</strong> One student sets a target number of squares, while the second student uses the split and merge tools to construct the exact arrangement before submitting.</li>\n      <li><strong>Efficiency Race:</strong> Challenge pairs to reach the target number using the fewest total moves, encouraging strategic planning over simple trial and error.</li>\n    </ul>\n  </section>\n\n  <section>\n    <h3>2. Exploring Part\u2013Whole Relationships & Decompositions</h3>\n    <ul>\n      <li><strong>Visualizing Numbers:</strong> Have students create a specific target quantity (e.g., 8 squares) using different combinations of split sizes (such as 4+4, 5+3, or 4+2+2) to strengthen number sense.</li>\n      <li><strong>Group Discussion:</strong> Ask students to explain how splitting a square breaks a whole into smaller parts, and how merging combines smaller parts back into a whole.</li>\n    </ul>\n  </section>\n\n  <section>\n    <h3>3. Guided Inquiry & Reflection Questions</h3>\n    <p>Teachers can deepen critical thinking by prompting students during or after play with questions such as:</p>\n    <ul>\n      <li><em>\"How did you decide whether to split a large square or merge smaller ones first?\"</em></li>\n      <li><em>\"Can you form the target number in a different visual layout?\"</em></li>\n      <li><em>\"What is the theoretical minimum number of moves needed for this target?\"</em></li>\n    </ul>\n  </section>\n</article>"
              },
              {
                "name": "Width",
                "value": "\"100%\""
              }
            ]
          }
        ]
      }
    ],
    "RootProperties": []
  },
  "metadata": {
    "APP": "WebEJS",
    "CreatedWith": "WebEJS : The web version of Easy JavaScript Simulations",
    "MoreInfo": "WebEJS 1.1",
    "version": "https://www.um.es/fem/wikis/webejs/"
  }
}