[
  {
    "path": ".dockerignore",
    "content": "# flyctl launch added from .gitignore\n# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\nnode_modules\n**/game/node_modules\n.pnp\n**/.pnp.js\n\n# testing\ncoverage\n\n# next.js\n.next\nout\n\n# production\nbuild\n\n# misc\n**/.DS_Store\n**/*.pem\n\n# debug\n**/npm-debug.log*\n**/yarn-debug.log*\n**/yarn-error.log*\n\n# local env files\n**/.env*.local\n\n# vercel\n**/.vercel\n\n# typescript\n**/*.tsbuildinfo\n**/next-env.d.ts\n**/.env\n.env.prod\nfly.toml\n\n# Vite build\n**/dist\n\n**/convex-local*\n**/convex_local*\n"
  },
  {
    "path": ".eslintignore",
    "content": "webpack*\n.eslintrc.js\nnext.config.js\ntailwind.config.js\npostcss.config.js\nconvex/_generated/*\ndist/*\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "export default {\n  parser: '@typescript-eslint/parser', // Specifies the ESLint parser\n  plugins: ['@typescript-eslint'],\n  extends: [\n    'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin\n    'plugin:@typescript-eslint/recommended-type-checked',\n  ],\n  parserOptions: {\n    project: './tsconfig.json',\n    ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features\n    sourceType: 'module', // Allows for the use of imports\n  },\n  rules: {\n    '@typescript-eslint/no-explicit-any': 'off',\n    '@typescript-eslint/explicit-function-return-type': 'off',\n    '@typescript-eslint/no-unused-vars': [\n      'warn',\n      { varsIgnorePattern: '^_', argsIgnorePattern: '^_' },\n    ],\n    '@typescript-eslint/no-non-null-assertion': 'off',\n  },\n};\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\ngame/node_modules/\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env*.local\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n.env\n/.env.prod\n\n# Vite build\ndist\n\nconvex-local*\nconvex_local*\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"trailingComma\": \"all\",\n  \"singleQuote\": true,\n  \"bracketSpacing\": true,\n  \"tabWidth\": 2,\n  \"proseWrap\": \"always\",\n  \"printWidth\": 100\n}\n"
  },
  {
    "path": ".vercelignore",
    "content": "convex-local*\nconvex_local*\n"
  },
  {
    "path": ".vscode/convex.code-snippets",
    "content": "{\n  \"Convex Imports\": {\n    \"prefix\": \"convex:imports\",\n    \"body\": [\n      \"import { v } from \\\"convex/values\\\";\",\n      \"import { api, internal } from \\\"./_generated/api\\\";\",\n      \"import { Doc, Id } from \\\"./_generated/dataModel\\\";\",\n      \"import {\",\n      \"  action,\",\n      \"  internalAction,\",\n      \"  internalMutation,\",\n      \"  internalQuery,\",\n      \"  mutation,\",\n      \"  query,\",\n      \"} from \\\"./_generated/server\\\";\"\n    ],\n    \"scope\": \"javascript,typescript\",\n    \"isFileTemplate\": true\n  },\n\n  \"Convex Query\": {\n    \"prefix\": \"convex:query\",\n    \"body\": [\n      \"export const $1 = query({\",\n      \"  args: {},\",\n      \"  handler: async (ctx, args) => {\",\n      \"    $0\",\n      \"  },\",\n      \"});\"\n    ],\n    \"scope\": \"javascript,typescript\"\n  },\n\n  \"Convex Internal Query\": {\n    \"prefix\": \"convex:internalQuery\",\n    \"body\": [\n      \"export const $1 = internalQuery({\",\n      \"  args: {},\",\n      \"  handler: async (ctx, args) => {\",\n      \"    $0\",\n      \"  },\",\n      \"});\"\n    ],\n    \"scope\": \"javascript,typescript\"\n  },\n\n  \"Convex Mutation\": {\n    \"prefix\": \"convex:mutation\",\n    \"body\": [\n      \"export const $1 = mutation({\",\n      \"  args: {},\",\n      \"  handler: async (ctx, args) => {\",\n      \"    $0\",\n      \"  },\",\n      \"});\"\n    ],\n    \"scope\": \"javascript,typescript\"\n  },\n\n  \"Convex Internal Mutation\": {\n    \"prefix\": \"convex:internalMutation\",\n    \"body\": [\n      \"export const $1 = internalMutation({\",\n      \"  args: {},\",\n      \"  handler: async (ctx, args) => {\",\n      \"    $0\",\n      \"  },\",\n      \"});\"\n    ],\n    \"scope\": \"javascript,typescript\"\n  },\n\n  \"Convex Action\": {\n    \"prefix\": \"convex:action\",\n    \"body\": [\n      \"import { action } from \\\"./_generated/server\\\";\",\n      \"\",\n      \"export const $1 = action({\",\n      \"  args: {},\",\n      \"  handler: async (ctx, args) => {\",\n      \"    $0\",\n      \"  },\",\n      \"});\"\n    ],\n    \"scope\": \"javascript,typescript\"\n  },\n\n  \"Convex Internal Action\": {\n    \"prefix\": \"convex:internalAction\",\n    \"body\": [\n      \"import { internalAction } from \\\"./_generated/server\\\";\",\n      \"\",\n      \"export const $1 = internalAction({\",\n      \"  args: {},\",\n      \"  handler: async (ctx, args) => {\",\n      \"    $0\",\n      \"  },\",\n      \"});\"\n    ],\n    \"scope\": \"javascript,typescript\"\n  }\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"editor.formatOnSave\": true,\n  \"editor.tabSize\": 2,\n  \"[html]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"[javascript]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"[jsonc]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"[typescript]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"[typescriptreact]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"typescript.preferences.importModuleSpecifierEnding\": \"auto\",\n  \"javascript.preferences.importModuleSpecifierEnding\": \"auto\"\n}\n"
  },
  {
    "path": "ARCHITECTURE.md",
    "content": "# Architecture\n\nThis documents dives into the high-level architecture of AI Town and its different layers. We'll\nfirst start with a brief overview and then go in-depth on each component. The overview should\nbe sufficient for forking AI Town and changing game or agent behavior. Read on to the deep dives\nif you're interested or running up against the engine's limitations.\n\nThis doc assumes the reader has a working knowledge of Convex. If you're new to Convex, check out\nthe [Convex tutorial](https://docs.convex.dev/get-started) to get started.\n\n## Overview\n\nAI Town is split into a few layers:\n\n- The server-side game logic in `convex/aiTown`: This layer defines what state AI Town maintains,\n  how it evolves over time, and how it reacts to user input. Both humans and agents submit inputs\n  that the game engine processes.\n- The client-side game UI in `src/`: AI Town uses `pixi-react` to render the game state to the\n  browser for human consumption.\n- The game engine in `convex/engine`: To make it easy to hack on the game rules, we've separated\n  out the game engine from the AI Town-specific game rules. The game engine is responsible for\n  saving and loading game state from the database, coordinating feeding inputs into the engine,\n  and actually running the game engine in Convex functions.\n- The agent in `convex/agent`: Agents run as part of the game loop, and can kick off asynchronous\n  Convex functions to do longer processing, such as talking to LLMs. Those functions can save state\n  in separate tables, or submit inputs to the game engine to modify game state. Internally, our\n  agents use a combination of simple rule-based systems and talking to an LLM.\n\nSo, if you'd like to tweak agent behavior but keep the same game mechanics, check out `convex/agent`\nfor the async work, and `convex/aiTown/agent.ts` for the game loop logic.\nIf you would like to add new gameplay elements (that both humans and agents can interact with), add\nthe feature to `convex/aiTown`, render it in the UI in `src/`, and respond to it in `convex/aiTown/agent.ts`.\n\nIf you have parts of your game that are more latency sensitive, you can move them out of engine\ninto regular Convex tables, queries, and mutations, only logging key bits into game state. See\n\"Message data model\" below for an example.\n\n## AI Town game logic (`convex/aiTown`)\n\n### Data model\n\nAI Town's data model has a few concepts:\n\n- Worlds (`convex/aiTown/world.ts`) represent a map with many players interacting together.\n- Players (`convex/aiTown/player.ts`) are the core characters in the game. Players have human readable names and\n  descriptions, and they may be associated with a human user. At any point in time, a player may be pathfinding\n  towards some destination and has a current location.\n- Conversations (`convex/aiTown/conversations.ts`) are created by a player and end at some point in time.\n- Conversation memberships (`convex/aiTown/conversationMembership.ts`) indicate that a player is a member\n  of a conversation. Players may only be in one conversation at any point in time, and conversations\n  currently have exactly two members. Memberships may be in one of three states:\n  - `invited`: The player has been invited to the conversation but hasn't accepted yet.\n  - `walkingOver`: The player has accepted the invite to the conversation but is too far away to talk. The\n    player will automatically join the conversation when they get close enough.\n  - `participating`: The player is actively participating in the conversation.\n\n### Schema\n\nThere are three main categories of tables:\n\n1. Engine tables (`convex/engine/schema.ts`) for maintaining engine-internal state.\n2. Game tables (`convex/aiTown/schema.ts`) for game state. To keep game state small and efficient to\n   read and write, we store AI Town's data model across a few tables. See `convex/aiTown/schema.ts` for an overview.\n3. Agent tables (`convex/agent/schema.ts`) for agent state. Agents can freely read and write to these tables\n   within their actions.\n\n### Inputs (`convex/aiTown/inputs.ts`)\n\nAI Town modifies its data model by processing inputs. Inputs are submitted by players and agents and\nprocessed by the game engine. We specify inputs in the `inputs` object in `convex/aiTown/inputs.ts`.\nUse the `inputHandler` function to construct an input handler, specifying a Convex validator for\narguments for end-to-end type-safety.\n\n- Joining (`join`) and leaving (`leave`) the game.\n- Moving a player to a particular location (`moveTo`): Movement in AI Town is similar to RTS games, where\n  the players specify where they want to go, and the engine figures out how to get there.\n- Starting a conversation (`startConversation`), accepting an invite (`acceptInvite`), rejecting an invite\n  (`rejectInvite`), and leaving a conversation (`leaveConversation`). To track typing indicators,\n  you use `startTyping` and `finishSendingMessage`. These are imported from `game/conversations.ts`.\n- Agent inputs are imported from `aiTown/agentInputs.ts` for things like remembering conversations,\n  deciding what to do, etc.\n\nEach of these inputs' implementation method checks invariants and updates game state as desired.\nFor example, the `moveTo` input checks that the player isn't participating in a conversation,\nthrowing an error telling them to leave the conversation first if so, and then updates their\npathfinding state with the desired destination.\n\n### Simulation\n\nOther than when processing player inputs, the game state can change over time in the background as the\nsimulation runs time forward. For example, if a player has decided to move along a path, their position\nwill gradually update as time moves forward. Similarly, if two players collide into each other, they'll\nnotice and replan their paths, trying to avoid obstacles.\n\n### Message data model\n\nWe manage the tables for tracking chat messages in separate tables not affiliated\nwith the game engine. This is for a few reasons:\n\n- The core simulation doesn't need to know about messages, so keeping them\n  out keeps game state small.\n- Messages are updated very frequently (when streamed out from OpenAI) and\n  benefit from lower input latency, so they're not a great fit for the engine.\n  See \"Design goals and limitations\" below.\n\nMessages (`convex/schema.ts`) are in a conversation and indicate an author and message text.\nEach conversation has a typing state in the conversations table that indicates that a player\nis currently typing. Players can still send messages while another player is typing, but\nhaving the indicator helps agents (and humans) not talk over each other.\n\nThe separate tables are queried and modified with regular Convex queries and mutations\nthat don't directly go through the simulation.\n\n## Game engine (`convex/engine`)\n\nGiven the description of AI Town's game behavior in the previous section,\nthe `AbstractGame` class in `convex/engine/abstractGame.ts` implements actually running the simulation.\nThe game engine has a few responsibilities:\n\n- Coordinating incoming player inputs, feeding them into the simulation, and sending their\n  return values (or errors) to the client.\n- Running the simulation forward in time.\n- Saving and loading game state from the database.\n- Managing executing the game behavior, efficiently using Convex resources and minimizing input latency.\n\nAI Town's game behavior is implemented in the `Game` subclass.\n\n### Input handling\n\nUsers submit inputs through the `insertInput` function, which inserts them into an `inputs` table, assigning a\nmonotonically increasing unique input number and stamping the input with the time the server received it. The\nengine then processes inputs, writing their results back to the `inputs` row. Interested clients can subscribe\non an input's status with the `inputStatus` query.\n\n`Game` provides an abstract method `handleInput` that `AiTown` implements with its specific behavior.\n\n### Running the simulation\n\nThe `Game` class specifies how it simulates time forward with the `tick` method:\n\n- `tick(now)` runs the simulation forward until the given timestamp\n- Ticks are run at a high frequency, configurable with `tickDuration` (milliseconds). Since AI town has smooth motion\n  for player movement, it runs at 60 ticks per second.\n- It's generally a good idea to break up game logic into separate systems that can be ticked forward independently.\n  For example, AI Town's `tick` method advances pathfinding with `Player.tickPathfinding`, player positions with\n  `Player.tickPosition`, conversations with `Conversation.tick`, and `Agent.tick` for agent logic.\n\nTo avoid running a Convex mutation 60 times per second (which would be expensive and slow), the engine batches up\nmany ticks into a _step_. AI town runs steps at only 1 time per second. Here's how a step works:\n\n1. Load the game state into memory.\n2. Decide how long to run.\n3. Execute many ticks for our time interval, alternating between feeding in inputs with `handleInput` and advancing\n   the simulation with `tick`.\n4. Write the updated game state back to the database.\n\nOne core invariant is that the game engine is fully \"single-threaded\" per world, so there are never two runs of\nan engine's step overlapping in time. Not having to think about race conditions or concurrency makes writing game\nengine code a lot easier.\n\nHowever, preserving this invariant is a little tricky. If the engine is idle for a minute and an\ninput comes in, we want to run the engine immediately but then cancel its run after the minute's\nup. If we're not careful, a race condition may cause us to run multiple copies of the engine if an\ninput comes in just as an idle timeout is expiring!\n\nOur approach is to store a generation number with the engine that monotonically increases over time.\nAll scheduled runs of the engine contain their expected generation number as an argument. Then, if\nwe'd like to cancel a future run of the engine, we can bump the generation number by one, and then\nwe're guaranteed that the subsequent run will fail immediately as it'll notice that the engine's\ngeneration number does not match its expected one.\n\n### Engine state management\n\nThe `World`, `Player`, `Conversation`, and `Agent` classes coordinate loading data into memory from the database,\nmodifying it according to the game rules, and serializing it to write back out to the database. Here's the flow:\n\n1. The Convex scheduler calls the `convex/aiTown/main.ts:runStep` action.\n2. The `runStep` action calls `convex/aiTown/game.ts:loadWorld` to load the current game state. This query calls\n   `Game.load`, which loads all of a world's game state from the appropriate tables, and returns a\n   `GameState` object, which contains serialized versions of all of the players, agents, etc.\n3. The `runStep` action passes the `GameState` to the `Game` constructor, which parses the serialized versions\n   of all our game objects using their constructors. For example, `new Player(serializedPlayer)` parses the\n   database representation into the in-memory `Player` class.\n4. The engine runs the simulation, modifying the in-memory game objects.\n5. At the end of a step, the framework calls `Game.saveStep`, which computes a diff of the game state since\n   the beginning of the step and passes the diff to the `convex/aiTown/game.ts:saveWorld` mutation.\n6. The `saveWorld` mutation applies the diff to the database, notices if any deleted objects need to be archived,\n   updates the `participatedTogether` graph, and kicks off any scheduled jobs to run.\n7. Since the engine is the only mutator of game state, it continues to run steps for some amount of time\n   without repeating steps 1 to 3 again.\n\nJust as we assume that the game engine is \"single threaded\", we also assume that the game engine _exclusively_\nowns the tables that store game engine state. Only the game engine should programmatically modify these tables,\nso components outside the engine can only mutate them by sending inputs.\n\n### Historical tables\n\nIf we're only writing updates out to the database at the end of the step, and steps are only running at once per\nsecond, continuous quantities like position will only update every second. This, then, defeats the whole purpose\nof having high-frequency ticks: Player positions will jump around and look choppy.\n\nTo solve this, we track the historical values of quantities like position _within_ a step, storing the value\nat the end of each tick. Then, the client receives both the current value _and_ the past step's worth of\nhistory, and it can \"replay\" the history to make the motion smooth.\n\nThe game tracks these quantities at the end of each tick by feeding them to a `HistoricalObject`. This object\nefficiently tracks its changes over time and serializes them into a buffer that clients can use for replaying\nits history. There are a few limitations on `HistoricalObject`:\n\n- Historical objects can only have numeric (floating point) values and can't have nested objects or optional fields.\n- Historical objects must declare which fields they'd like to track.\n\nWe store each player's \"location\" (i.e. its position, orientation, and speed) in a `HistoricalObject` and\nwrite it to the `worlds` document at the end of a step when computing a diff.\n\n## Client-side game UI (`src/`)\n\nOne guiding principle for AI Town's architecture is to keep the usage as close to \"regular Convex\" usage as possible. So,\ngame state is stored in regular tables, and the UI just uses regular `useQuery` hooks to load that state and render\nit in the UI.\n\nThe one exception is for historical tables, which feed in the latest state into a `useHistoricalValue` hook that parses\nthe history buffer and replays time forward for smooth motion. To keep replayed time synchronized across multiple\nhistorical buffers, we provide a `useHistoricalTime` hook for the top of your app that keeps track of the current\ntime and returns it for you to pass down into components.\n\nWe also provide a `useSendInput` hook that wraps `useMutation` and automatically sends inputs to the server and\nwaits for the engine to process them and return their outcome.\n\n## Agent architecture (`convex/agent`)\n\n### The agent loop (`convex/game/agents.ts`)\n\nAgents will execute any game state changes, and schedule operations to do anything that requires\na long-lived request or accessing non-game tables. The flow generally is:\n\n1. Logic in `Agent.tick` can read and modify game state as time progresses, such as waiting until\n   the agent is near another player to start talking.\n2. When there is something that needs to talk to an LLM or read/write external data,\n   it calls `startOperation` with a reference to a Convex function: generally an `internalAction`.\n3. This function can read state from game tables and other tables via `internalQuery` functions.\n4. It executes long-running tasks, and can write data via `internalMutation`s.\n   Game state should not be written, but rather submitted via `inputs` (described in a previous section).\n5. Inputs are submitted from actions with `ctx.runMutation(api.game.main.sendInput, {...})` from actions\n   or via `insertInput` from mutations. They are referenced by their name as a string, like `moveTo`.\n6. Inputs are defined with `inputHandler` and are given an instance of the AiTown game to modify,\n   similar to the game loop. In fact, these are called as part of the game loop before `tickAgent`.\n7. When an operation is done, it deletes the `inProgressOperation`. This is to ensure an agent only\n   is trying to do one thing at a time.\n8. `Agent.tick` then can observe the new game state and continue to make decisions.\n\n### Conversations (`convex/agent/conversations.ts`)\n\nThe agent code calls into the conversation layer which implements the prompt engineering for\ninjecting personality and memories into the GPT responses. It has functions for starting a\nconversation (`startConversation`), continuing after the first message (`continueConversation`), and\npolitely leaving a conversation (`leaveConversation`). Each function loads structured data from the\ndatabase, queries the memory layer for the agent's opinion about the player they're talking with,\nand then calls into the OpenAI client (`convex/util/openai.ts`).\n\n### Memories (`convex/agent/memory.ts`)\n\nAfter each conversation, GPT summarizes its message history, and we compute an embedding of the\nsummary text and write it into Convex's vector database. Then, when starting a new conversation\nwith, Danny, we embed \"What you think about Danny?\", find the three most similar memories, and fetch\ntheir summary texts to inject into the conversation prompt.\n\n### Embeddings cache (`convex/agent/embeddingsCache.ts`)\n\nTo avoid computing the same embedding over and over again, we cache embeddings by a hash of their\ntext in a Convex table.\n\n## Design goals and limitations\n\nAI Town's game engine has a few design goals:\n\n- Try to be as close to a regular Convex app as possible. Use regular client hooks (like `useQuery`)\n  when possible, and store game state in regular tables.\n- Be as similar to existing engines as possible, so it's easy to change the behavior. We chose a\n  `tick()` based model for simulation since it's commonly used elsewhere and intuitive.\n- Decouple agent behavior from the game engine. It's nice to allow human players and AI agents to do\n  all the same things in the game.\n\nThese design goals imply some inherent limitations:\n\n- All data is loaded into memory each step. The active game state loaded by the game should be small\n  enough to fit into memory and load and save frequently. Try to keep game state to less than a few dozen\n  kilobytes: Games that require tens of thousands of objects interacting together may not be a good\n  fit.\n- All inputs are fed through the database in the `inputs` table, so applications that require very\n  large or frequent inputs may not be a good fit.\n- Input latency will be around one RTT (time for the input to make it to the server and the response\n  to come back) plus half the step size (for expected server input delay when the input's waiting\n  for the next step). Historical values add another half step size of input latency since their\n  values are viewed slightly in the past. As configured, this will roughly be around 1.5s of input\n  latency, which won't be a good fit for competitive games. You can configure the step size to be\n  smaller (e.g. 250ms) which will decrease input latency at the cost of adding more Convex function\n  calls and database bandwidth.\n- The game engine is designed to be single threaded. JavaScript operating over plain objects\n  in-memory can be surprisingly fast, but if your simulation is very computationally expensive, it\n  may not be a good fit on AI Town's engine today.\n"
  },
  {
    "path": "Dockerfile",
    "content": "# Use an Ubuntu base image\nFROM ubuntu:22.04\n\n# Install dependencies\nRUN apt-get update && \\\n    apt-get install -y \\\n    curl \\\n    python3 \\\n    python3-pip \\\n    unzip \\\n    socat \\\n    build-essential \\\n    libssl-dev \\\n    iproute2 \\\n    && rm -rf /var/lib/apt/lists/*\n\n# Install NVM, Node.js, and npm\nRUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash && \\\n    export NVM_DIR=\"$HOME/.nvm\" && \\\n    [ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\" && \\\n    nvm install 18 && \\\n    nvm use 18\n\n# Add NVM to PATH\nENV NVM_DIR /root/.nvm\nENV NODE_VERSION 18.0.0\nRUN . $NVM_DIR/nvm.sh && nvm install $NODE_VERSION\nENV NODE_PATH $NVM_DIR/versions/node/v$NODE_VERSION/lib/node_modules\nENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH\n\n# Set the working directory\nWORKDIR /usr/src/app\n\n# Copy dependency files\nCOPY package*.json ./\n\n# Install npm dependencies\nRUN npm install\n\nRUN npx update-browserslist-db@latest\n\n# Copy application files\nCOPY . .\n\n# Expose necessary ports\nEXPOSE 5173\n\nCMD [\"npx\", \"vite\", \"--host\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 a16z-infra\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# AI Town 🏠💻💌\n\n[Live Demo](https://www.convex.dev/ai-town)\n\n[Join our community Discord: AI Stack Devs](https://discord.gg/PQUmTBTGmT)\n\n<img width=\"1454\" alt=\"Screen Shot 2023-08-14 at 10 01 00 AM\" src=\"https://github.com/a16z-infra/ai-town/assets/3489963/a4c91f17-23ed-47ec-8c4e-9f9a8505057d\">\n\nAI Town is a virtual town where AI characters live, chat and socialize.\n\nThis project is a deployable starter kit for easily building and customizing your own version of AI\ntown. Inspired by the research paper\n[_Generative Agents: Interactive Simulacra of Human Behavior_](https://arxiv.org/pdf/2304.03442.pdf).\n\nThe primary goal of this project, beyond just being a lot of fun to work on, is to provide a\nplatform with a strong foundation that is meant to be extended. The back-end natively supports\nshared global state, transactions, and a simulation engine and should be suitable from everything\nfrom a simple project to play around with to a scalable, multi-player game. A secondary goal is to\nmake a JS/TS framework available as most simulators in this space (including the original paper\nabove) are written in Python.\n\n## Overview\n\n- 💻 [Stack](#stack)\n- 🧠 [Installation](#installation) (cloud, local, Docker, self-host, Fly.io, ...)\n- 💻️ [Windows Pre-requisites](#windows-installation)\n- 🤖 [Configure your LLM of choice](#connect-an-llm) (Ollama, OpenAI, Together.ai, ...)\n- 👤 [Customize - YOUR OWN simulated world](#customize-your-own-simulation)\n- 👩‍💻 [Deploying to production](#deploy-the-app-to-production)\n- 🐛 [Troubleshooting](#troubleshooting)\n\n## Stack\n\n- Game engine, database, and vector search: [Convex](https://convex.dev/)\n- Auth (Optional): [Clerk](https://clerk.com/)\n- Default chat model is `llama3` and embeddings with `mxbai-embed-large`.\n- Local inference: [Ollama](https://github.com/jmorganca/ollama)\n- Configurable for other cloud LLMs: [Together.ai](https://together.ai/) or anything that speaks the\n  [OpenAI API](https://platform.openai.com/). PRs welcome to add more cloud provider support.\n- Background Music Generation: [Replicate](https://replicate.com/) using\n  [MusicGen](https://huggingface.co/spaces/facebook/MusicGen)\n\nOther credits:\n\n- Pixel Art Generation: [Replicate](https://replicate.com/),\n  [Fal.ai](https://serverless.fal.ai/lora)\n- All interactions, background music and rendering on the <Game/> component in the project are\n  powered by [PixiJS](https://pixijs.com/).\n- Tilesheet:\n  - https://opengameart.org/content/16x16-game-assets by George Bailey\n  - https://opengameart.org/content/16x16-rpg-tileset by hilau\n- We used https://github.com/pierpo/phaser3-simple-rpg for the original POC of this project. We have\n  since re-wrote the whole app, but appreciated the easy starting point\n- Original assets by [ansimuz](https://opengameart.org/content/tiny-rpg-forest)\n- The UI is based on original assets by\n  [Mounir Tohami](https://mounirtohami.itch.io/pixel-art-gui-elements)\n\n# Installation\n\nThe overall steps are:\n\n1. [Build and deploy](#build-and-deploy)\n2. [Connect it to an LLM](#connect-an-llm)\n\n## Build and Deploy\n\nThere are a few ways to run the app on top of Convex (the backend).\n\n1. The standard Convex setup, where you develop locally or in the cloud. This requires a Convex\n   account(free). This is the easiest way to depoy it to the cloud and seriously develop.\n2. If you want to try it out without an account and you're okay with Docker, the Docker Compose\n   setup is nice and self-contained.\n3. There's a community fork of this project offering a one-click install on\n   [Pinokio](https://pinokio.computer/item?uri=https://github.com/cocktailpeanutlabs/aitown) for\n   anyone interested in running but not modifying it 😎.\n4. You can also deploy it to [Fly.io](https://fly.io/). See [./fly](./fly) for instructions.\n\n### Standard Setup\n\nNote, if you're on Windows, see [below](#windows-installation).\n\n```sh\ngit clone https://github.com/a16z-infra/ai-town.git\ncd ai-town\nnpm install\n```\n\nThis will require logging into your Convex account, if you haven't already.\n\nTo run it:\n\n```sh\nnpm run dev\n```\n\nYou can now visit http://localhost:5173.\n\nIf you'd rather run the frontend and backend separately (which syncs your backend functions as\nthey're saved), you can run these in two terminals:\n\n```bash\nnpm run dev:frontend\nnpm run dev:backend\n```\n\nSee [package.json](./package.json) for details.\n\n### Using Docker Compose with self-hosted Convex\n\nYou can also run the Convex backend with the self-hosted Docker container. Here we'll set it up to\nrun the frontend, backend, and dashboard all via docker compose.\n\n```sh\ndocker compose up --build -d\n```\n\nThe container will keep running in the background if you pass `-d`. After you've done it once, you\ncan `stop` and `start` services.\n\n- The frontend will be running on http://localhost:5173.\n- The backend will be running on http://localhost:3210 (3211 for the http api).\n- The dashboard will be running on http://localhost:6791.\n\nTo log into the dashboard and deploy from the convex CLI, you will need to generate an admin key.\n\n```sh\ndocker compose exec backend ./generate_admin_key.sh\n```\n\nAdd it to your `.env.local` file. Note: If you run `down` and `up`, you'll have to generate the key\nagain and update the `.env.local` file.\n\n```sh\n# in .env.local\nCONVEX_SELF_HOSTED_ADMIN_KEY=\"<admin-key>\" # Ensure there are quotes around it\nCONVEX_SELF_HOSTED_URL=\"http://127.0.0.1:3210\"\n```\n\nThen set up the Convex backend (one time):\n\n```sh\nnpm run predev\n```\n\nTo continuously deploy new code to the backend and print logs:\n\n```sh\nnpm run dev:backend\n```\n\nTo see the dashboard, visit `http://localhost:6791` and provide the admin key you generated earlier.\n\n### Configuring Docker for Ollama\n\nIf you'll be using Ollama for local inference, you'll need to configure Docker to connect to it.\n\n```sh\nnpx convex env set OLLAMA_HOST http://host.docker.internal:11434\n```\n\nTo test the connection (after you [have it running](#ollama-default)):\n\n```sh\ndocker compose exec backend /bin/bash curl http://host.docker.internal:11434\n```\n\nIf it says \"Ollama is running\", it's good! Otherwise, check out the\n[Troubleshooting](#troubleshooting) section.\n\n## Connect an LLM\n\nNote: If you want to run the backend in the cloud, you can either use a cloud-based LLM API, like\nOpenAI or Together.ai or you can proxy the traffic from the cloud to your local Ollama. See\n[below](#using-local-inference-from-a-cloud-deployment) for instructions.\n\n### Ollama (default)\n\nBy default, the app tries to use Ollama to run it entirely locally.\n\n1. Download and install [Ollama](https://ollama.com/).\n2. Open the app or run `ollama serve` in a terminal. `ollama serve` will warn you if the app is\n   already running.\n3. Run `ollama pull llama3` to have it download `llama3`.\n4. Test it out with `ollama run llama3`.\n\nOllama model options can be found [here](https://ollama.ai/library).\n\nIf you want to customize which model to use, adjust convex/util/llm.ts or set\n`npx convex env set OLLAMA_MODEL # model`. If you want to edit the embedding model:\n\n1. Change the `OLLAMA_EMBEDDING_DIMENSION` in `convex/util/llm.ts` and ensure:\n   `export const EMBEDDING_DIMENSION = OLLAMA_EMBEDDING_DIMENSION;`\n2. Set `npx convex env set OLLAMA_EMBEDDING_MODEL # model`.\n\nNote: You might want to set `NUM_MEMORIES_TO_SEARCH` to `1` in constants.ts, to reduce the size of\nconversation prompts, if you see slowness.\n\n### OpenAI\n\nTo use OpenAI, you need to:\n\n```ts\n// In convex/util/llm.ts change the following line:\nexport const EMBEDDING_DIMENSION = OPENAI_EMBEDDING_DIMENSION;\n```\n\nSet the `OPENAI_API_KEY` environment variable. Visit https://platform.openai.com/account/api-keys if\nyou don't have one.\n\n```sh\nnpx convex env set OPENAI_API_KEY 'your-key'\n```\n\nOptional: choose models with `OPENAI_CHAT_MODEL` and `OPENAI_EMBEDDING_MODEL`.\n\n### Together.ai\n\nTo use Together.ai, you need to:\n\n```ts\n// In convex/util/llm.ts change the following line:\nexport const EMBEDDING_DIMENSION = TOGETHER_EMBEDDING_DIMENSION;\n```\n\nSet the `TOGETHER_API_KEY` environment variable. Visit https://api.together.xyz/settings/api-keys if\nyou don't have one.\n\n```sh\nnpx convex env set TOGETHER_API_KEY 'your-key'\n```\n\nOptional: choose models via `TOGETHER_CHAT_MODEL`, `TOGETHER_EMBEDDING_MODEL`. The embedding model's\ndimension must match `EMBEDDING_DIMENSION`.\n\n### Other OpenAI-compatible API\n\nYou can use any OpenAI-compatible API, such as Anthropic, Groq, or Azure.\n\n- Change the `EMBEDDING_DIMENSION` in `convex/util/llm.ts` to match the dimension of your embedding\n  model.\n- Edit `getLLMConfig` in `llm.ts` or set environment variables:\n\n```sh\nnpx convex env set LLM_API_URL 'your-url'\nnpx convex env set LLM_API_KEY 'your-key'\nnpx convex env set LLM_MODEL 'your-chat-model'\nnpx convex env set LLM_EMBEDDING_MODEL 'your-embedding-model'\n```\n\nNote: if `LLM_API_KEY` is not required, don't set it.\n\n### Note on changing the LLM provider or embedding model:\n\nIf you change the LLM provider or embedding model, you should delete your data and start over. The\nembeddings used for memory are based on the embedding model you choose, and the dimension of the\nvector database must match the embedding model's dimension. See\n[below](#wiping-the-database-and-starting-over) for how to do that.\n\n## Customize your own simulation\n\nNOTE: every time you change character data, you should re-run `npx convex run testing:wipeAllTables`\nand then `npm run dev` to re-upload everything to Convex. This is because character data is sent to\nConvex on the initial load. However, beware that `npx convex run testing:wipeAllTables` WILL wipe\nall of your data.\n\n1. Create your own characters and stories: All characters and stories, as well as their spritesheet\n   references are stored in [characters.ts](./data/characters.ts). You can start by changing\n   character descriptions.\n\n2. Updating spritesheets: in `data/characters.ts`, you will see this code:\n\n   ```ts\n   export const characters = [\n     {\n       name: 'f1',\n       textureUrl: '/assets/32x32folk.png',\n       spritesheetData: f1SpritesheetData,\n       speed: 0.1,\n     },\n     ...\n   ];\n   ```\n\n   You should find a sprite sheet for your character, and define sprite motion / assets in the\n   corresponding file (in the above example, `f1SpritesheetData` was defined in f1.ts)\n\n3. Update the Background (Environment): The map gets loaded in `convex/init.ts` from\n   `data/gentle.js`. To update the map, follow these steps:\n\n   - Use [Tiled](https://www.mapeditor.org/) to export tilemaps as a JSON file (2 layers named\n     bgtiles and objmap)\n   - Use the `convertMap.js` script to convert the JSON to a format that the engine can use.\n\n   ```console\n   node data/convertMap.js <mapDataPath> <assetPath> <tilesetpxw> <tilesetpxh>\n   ```\n\n   - `<mapDataPath>`: Path to the Tiled JSON file.\n   - `<assetPath>`: Path to tileset images.\n   - `<tilesetpxw>`: Tileset width in pixels.\n   - `<tilesetpxh>`: Tileset height in pixels. Generates `converted-map.js` that you can use like\n     `gentle.js`\n\n4. Adding background music with Replicate (Optional)\n\n   For Daily background music generation, create a [Replicate](https://replicate.com/) account and\n   create a token in your Profile's [API Token page](https://replicate.com/account/api-tokens).\n   `npx convex env set REPLICATE_API_TOKEN # token`\n\n   This only works if you can receive the webhook from Replicate. If it's running in the normal\n   Convex cloud, it will work by default. If you're self-hosting, you'll need to configure it to hit\n   your app's url on `/http`. If you're using Docker Compose, it will be `http://localhost:3211`,\n   but you'll need to proxy the traffic to your local machine.\n\n   **Note**: The simulation will pause after 5 minutes if the window is idle. Loading the page will\n   unpause it. You can also manually freeze & unfreeze the world with a button in the UI. If you\n   want to run the world without the browser, you can comment-out the \"stop inactive worlds\" cron in\n   `convex/crons.ts`.\n\n   - Change the background music by modifying the prompt in `convex/music.ts`\n   - Change how often to generate new music at `convex/crons.ts` by modifying the\n     `generate new background music` job\n\n## Commands to run / test / debug\n\n**To stop the back end, in case of too much activity**\n\nThis will stop running the engine and agents. You can still run queries and run functions to debug.\n\n```bash\nnpx convex run testing:stop\n```\n\n**To restart the back end after stopping it**\n\n```bash\nnpx convex run testing:resume\n```\n\n**To kick the engine in case the game engine or agents aren't running**\n\n```bash\nnpx convex run testing:kick\n```\n\n**To archive the world**\n\nIf you'd like to reset the world and start from scratch, you can archive the current world:\n\n```bash\nnpx convex run testing:archive\n```\n\nThen, you can still look at the world's data in the dashboard, but the engine and agents will no\nlonger run.\n\nYou can then create a fresh world with `init`.\n\n```bash\nnpx convex run init\n```\n\n**To pause your backend deployment**\n\nYou can go to the [dashboard](https://dashboard.convex.dev) to your deployment settings to pause and\nun-pause your deployment. This will stop all functions, whether invoked from the client, scheduled,\nor as a cron job. See this as a last resort, as there are gentler ways of stopping above.\n\n## Windows Installation\n\n### Prerequisites\n\n1. **Windows 10/11 with WSL2 installed**\n2. **Internet connection**\n\nSteps:\n\n1. Install WSL2\n\n   First, you need to install WSL2. Follow\n   [this guide](https://docs.microsoft.com/en-us/windows/wsl/install) to set up WSL2 on your Windows\n   machine. We recommend using Ubuntu as your Linux distribution.\n\n2. Update Packages\n\n   Open your WSL terminal (Ubuntu) and update your packages:\n\n   ```sh\n   sudo apt update\n   ```\n\n3. Install NVM and Node.js\n\n   NVM (Node Version Manager) helps manage multiple versions of Node.js. Install NVM and Node.js 18\n   (the stable version):\n\n   ```sh\n   curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash\n   export NVM_DIR=\"$([ -z \"${XDG_CONFIG_HOME-}\" ] && printf %s \"${HOME}/.nvm\" || printf %s \"${XDG_CONFIG_HOME}/nvm\")\"\n   [ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\"\n   source ~/.bashrc\n   nvm install 18\n   nvm use 18\n   ```\n\n4. Install Python and Pip\n\n   Python is required for some dependencies. Install Python and Pip:\n\n   ```sh\n   sudo apt-get install python3 python3-pip sudo ln -s /usr/bin/python3 /usr/bin/python\n   ```\n\nAt this point, you can follow the instructions [above](#installation).\n\n## Deploy the app to production\n\n### Deploy Convex functions to prod environment\n\nBefore you can run the app, you will need to make sure the Convex functions are deployed to its\nproduction environment. Note: this is assuming you're using the default Convex cloud product.\n\n1. Run `npx convex deploy` to deploy the convex functions to production\n2. Run `npx convex run init --prod`\n\nTo transfer your local data to the cloud, you can run `npx convex export` and then import it with\n`npx convex import --prod`.\n\nIf you have existing data you want to clear, you can run\n`npx convex run testing:wipeAllTables --prod`\n\n### Adding Auth (Optional)\n\nYou can add clerk auth back in with `git revert b44a436`. Or just look at that diff for what changed\nto remove it.\n\n**Make a Clerk account**\n\n- Go to https://dashboard.clerk.com/ and click on \"Add Application\"\n- Name your application and select the sign-in providers you would like to offer users\n- Create Application\n- Add `VITE_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` to `.env.local`\n\n```bash\nVITE_CLERK_PUBLISHABLE_KEY=pk_***\nCLERK_SECRET_KEY=sk_***\n```\n\n- Go to JWT Templates and create a new Convex Template.\n- Copy the JWKS endpoint URL for use below.\n\n```sh\nnpx convex env set CLERK_ISSUER_URL # e.g. https://your-issuer-url.clerk.accounts.dev/\n```\n\n### Deploy the frontend to Vercel\n\n- Register an account on Vercel and then [install the Vercel CLI](https://vercel.com/docs/cli).\n- **If you are using Github Codespaces**: You will need to\n  [install the Vercel CLI](https://vercel.com/docs/cli) and authenticate from your codespaces cli by\n  running `vercel login`.\n- Deploy the app to Vercel with `vercel --prod`.\n\n## Using local inference from a cloud deployment\n\nWe support using [Ollama](https://github.com/jmorganca/ollama) for conversation generations. To have\nit accessible from the web, you can use Tunnelmole or Ngrok or similar so the cloud backend can send\nrequests to Ollama running on your local machine.\n\nSteps:\n\n1. Set up either Tunnelmole or Ngrok.\n2. Add Ollama endpoint to Convex\n   ```sh\n   npx convex env set OLLAMA_HOST # your tunnelmole/ngrok unique url from the previous step\n   ```\n3. Update Ollama domains Ollama has a list of accepted domains. Add the ngrok domain so it won't\n   reject traffic. see [ollama.ai](https://ollama.ai) for more details.\n\n### Using Tunnelmole\n\n[Tunnelmole](https://github.com/robbie-cahill/tunnelmole-client) is an open source tunneling tool.\n\nYou can install Tunnelmole using one of the following options:\n\n- NPM: `npm install -g tunnelmole`\n- Linux: `curl -s https://tunnelmole.com/sh/install-linux.sh | sudo bash`\n- Mac:\n  `curl -s https://tunnelmole.com/sh/install-mac.sh --output install-mac.sh && sudo bash install-mac.sh`\n- Windows: Install with NPM, or if you don't have NodeJS installed, download the `exe` file for\n  Windows [here](https://tunnelmole.com/downloads/tmole.exe) and put it somewhere in your PATH.\n\nOnce Tunnelmole is installed, run the following command:\n\n```\ntmole 11434\n```\n\nTunnelmole should output a unique url once you run this command.\n\n### Using Ngrok\n\nNgrok is a popular closed source tunneling tool.\n\n- [Install Ngrok](https://ngrok.com/docs/getting-started/)\n\nOnce ngrok is installed and authenticated, run the following command:\n\n```\nngrok http http://localhost:11434\n```\n\nNgrok should output a unique url once you run this command.\n\n## Troubleshooting\n\n### Wiping the database and starting over\n\nYou can wipe the database by running:\n\n```sh\nnpx convex run testing:wipeAllTables\n```\n\nThen reset with:\n\n```sh\nnpx convex run init\n```\n\n### Incompatible Node.js versions\n\nIf you encounter a node version error on the convex server upon application startup, please use node\nversion 18, which is the most stable. One way to do this is by\n[installing nvm](https://nodejs.org/en/download/package-manager) and running `nvm install 18` and\n`nvm use 18`.\n\n### Reaching Ollama\n\nIf you're having trouble with the backend communicating with Ollama, it depends on your setup how to\ndebug:\n\n1. If you're running directly on Windows, see\n   [Windows Ollama connection issues](#windows-ollama-connection-issues).\n2. If you're using **Docker**, see\n   [Docker to Ollama connection issues](#docker-to-ollama-connection-issues).\n3. If you're running locally, you can try the following:\n\n```sh\nnpx convex env set OLLAMA_HOST http://localhost:11434\n```\n\nBy default, the host is set to `http://127.0.0.1:11434`. Some systems prefer `localhost`\n¯\\_(ツ)\\_/¯.\n\n### Windows Ollama connection issues\n\nIf the above didn't work after following the [windows](#windows-installation) and regular\n[installation](#installation) instructions, you can try the following, assuming you're **not** using\nDocker.\n\nIf you're using Docker, see the [next section](#docker-to-ollama-connection-issues) for Docker\ntroubleshooting.\n\nFor running directly on Windows, you can try the following:\n\n1. Install `unzip` and `socat`:\n\n   ```sh\n   sudo apt install unzip socat\n   ```\n\n2. Configure `socat` to Bridge Ports for Ollama\n\n   Run the following command to bridge ports:\n\n   ```sh\n   socat TCP-LISTEN:11434,fork TCP:$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):11434 &\n   ```\n\n3. Test if it's working:\n\n   ```sh\n   curl http://127.0.0.1:11434\n   ```\n\n   If it responds OK, the Ollama API should be accessible.\n\n### Docker to Ollama connection issues\n\nIf you're having trouble with the backend communicating with Ollama, there's a couple things to\ncheck:\n\n1. Is Docker at least verion 18.03 ? That allows you to use the `host.docker.internal` hostname to\n   connect to the host from inside the container.\n\n2. Is Ollama running? You can check this by running `curl http://localhost:11434` from outside the\n   container.\n\n3. Is Ollama accessible from inside the container? You can check this by running\n   `docker compose exec backend curl http://host.docker.internal:11434`.\n\nIf 1 & 2 work, but 3 does not, you can use `socat` to bridge the traffic from inside the container\nto Ollama running on the host.\n\n1. Configure `socat` with the host's IP address (not the Docker IP).\n\n   ```sh\n   docker compose exec backend /bin/bash\n   HOST_IP=YOUR-HOST-IP\n   socat TCP-LISTEN:11434,fork TCP:$HOST_IP:11434\n   ```\n\n   Keep this running.\n\n2. Then from outside of the container:\n\n   ```sh\n   npx convex env set OLLAMA_HOST http://localhost:11434\n   ```\n\n3. Test if it's working:\n\n   ```sh\n   docker compose exec backend curl http://localhost:11434\n   ```\n\n   If it responds OK, the Ollama API is accessible. Otherwise, try changing the previous two to\n   `http://127.0.0.1:11434`.\n\n### Launching an Interactive Docker Terminal\n\nIf you wan to investigate inside the container, you can launch an interactive Docker terminal, for\nthe `frontend`, `backend` or `dashboard` service:\n\n```bash\ndocker compose exec frontend /bin/bash\n```\n\nTo exit the container, run `exit`.\n\n### Updating the browser list\n\n```bash\ndocker compose exec frontend npx update-browserslist-db@latest\n```\n\n# 🧑‍🏫 What is Convex?\n\n[Convex](https://convex.dev) is a hosted backend platform with a built-in database that lets you\nwrite your [database schema](https://docs.convex.dev/database/schemas) and\n[server functions](https://docs.convex.dev/functions) in\n[TypeScript](https://docs.convex.dev/typescript). Server-side database\n[queries](https://docs.convex.dev/functions/query-functions) automatically\n[cache](https://docs.convex.dev/functions/query-functions#caching--reactivity) and\n[subscribe](https://docs.convex.dev/client/react#reactivity) to data, powering a\n[realtime `useQuery` hook](https://docs.convex.dev/client/react#fetching-data) in our\n[React client](https://docs.convex.dev/client/react). There are also clients for\n[Python](https://docs.convex.dev/client/python), [Rust](https://docs.convex.dev/client/rust),\n[ReactNative](https://docs.convex.dev/client/react-native), and\n[Node](https://docs.convex.dev/client/javascript), as well as a straightforward\n[HTTP API](https://docs.convex.dev/http-api/).\n\nThe database supports [NoSQL-style documents](https://docs.convex.dev/database/document-storage)\nwith [opt-in schema validation](https://docs.convex.dev/database/schemas),\n[relationships](https://docs.convex.dev/database/document-ids) and\n[custom indexes](https://docs.convex.dev/database/indexes/) (including on fields in nested objects).\n\nThe [`query`](https://docs.convex.dev/functions/query-functions) and\n[`mutation`](https://docs.convex.dev/functions/mutation-functions) server functions have\ntransactional, low latency access to the database and leverage our\n[`v8` runtime](https://docs.convex.dev/functions/runtimes) with\n[determinism guardrails](https://docs.convex.dev/functions/runtimes#using-randomness-and-time-in-queries-and-mutations)\nto provide the strongest ACID guarantees on the market: immediate consistency, serializable\nisolation, and automatic conflict resolution via\n[optimistic multi-version concurrency control](https://docs.convex.dev/database/advanced/occ) (OCC /\nMVCC).\n\nThe [`action` server functions](https://docs.convex.dev/functions/actions) have access to external\nAPIs and enable other side-effects and non-determinism in either our\n[optimized `v8` runtime](https://docs.convex.dev/functions/runtimes) or a more\n[flexible `node` runtime](https://docs.convex.dev/functions/runtimes#nodejs-runtime).\n\nFunctions can run in the background via\n[scheduling](https://docs.convex.dev/scheduling/scheduled-functions) and\n[cron jobs](https://docs.convex.dev/scheduling/cron-jobs).\n\nDevelopment is cloud-first, with\n[hot reloads for server function](https://docs.convex.dev/cli#run-the-convex-dev-server) editing via\nthe [CLI](https://docs.convex.dev/cli),\n[preview deployments](https://docs.convex.dev/production/hosting/preview-deployments),\n[logging and exception reporting integrations](https://docs.convex.dev/production/integrations/),\nThere is a [dashboard UI](https://docs.convex.dev/dashboard) to\n[browse and edit data](https://docs.convex.dev/dashboard/deployments/data),\n[edit environment variables](https://docs.convex.dev/production/environment-variables),\n[view logs](https://docs.convex.dev/dashboard/deployments/logs),\n[run server functions](https://docs.convex.dev/dashboard/deployments/functions), and more.\n\nThere are built-in features for [reactive pagination](https://docs.convex.dev/database/pagination),\n[file storage](https://docs.convex.dev/file-storage),\n[reactive text search](https://docs.convex.dev/text-search),\n[vector search](https://docs.convex.dev/vector-search),\n[https endpoints](https://docs.convex.dev/functions/http-actions) (for webhooks),\n[snapshot import/export](https://docs.convex.dev/database/import-export/),\n[streaming import/export](https://docs.convex.dev/production/integrations/streaming-import-export),\nand [runtime validation](https://docs.convex.dev/database/schemas#validators) for\n[function arguments](https://docs.convex.dev/functions/args-validation) and\n[database data](https://docs.convex.dev/database/schemas#schema-validation).\n\nEverything scales automatically, and it’s [free to start](https://www.convex.dev/plans).\n"
  },
  {
    "path": "convex/_generated/api.d.ts",
    "content": "/* eslint-disable */\n/**\n * Generated `api` utility.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport type {\n  ApiFromModules,\n  FilterApi,\n  FunctionReference,\n} from \"convex/server\";\nimport type * as agent_conversation from \"../agent/conversation.js\";\nimport type * as agent_embeddingsCache from \"../agent/embeddingsCache.js\";\nimport type * as agent_memory from \"../agent/memory.js\";\nimport type * as aiTown_agent from \"../aiTown/agent.js\";\nimport type * as aiTown_agentDescription from \"../aiTown/agentDescription.js\";\nimport type * as aiTown_agentInputs from \"../aiTown/agentInputs.js\";\nimport type * as aiTown_agentOperations from \"../aiTown/agentOperations.js\";\nimport type * as aiTown_conversation from \"../aiTown/conversation.js\";\nimport type * as aiTown_conversationMembership from \"../aiTown/conversationMembership.js\";\nimport type * as aiTown_game from \"../aiTown/game.js\";\nimport type * as aiTown_ids from \"../aiTown/ids.js\";\nimport type * as aiTown_inputHandler from \"../aiTown/inputHandler.js\";\nimport type * as aiTown_inputs from \"../aiTown/inputs.js\";\nimport type * as aiTown_insertInput from \"../aiTown/insertInput.js\";\nimport type * as aiTown_location from \"../aiTown/location.js\";\nimport type * as aiTown_main from \"../aiTown/main.js\";\nimport type * as aiTown_movement from \"../aiTown/movement.js\";\nimport type * as aiTown_player from \"../aiTown/player.js\";\nimport type * as aiTown_playerDescription from \"../aiTown/playerDescription.js\";\nimport type * as aiTown_world from \"../aiTown/world.js\";\nimport type * as aiTown_worldMap from \"../aiTown/worldMap.js\";\nimport type * as constants from \"../constants.js\";\nimport type * as crons from \"../crons.js\";\nimport type * as engine_abstractGame from \"../engine/abstractGame.js\";\nimport type * as engine_historicalObject from \"../engine/historicalObject.js\";\nimport type * as http from \"../http.js\";\nimport type * as init from \"../init.js\";\nimport type * as messages from \"../messages.js\";\nimport type * as music from \"../music.js\";\nimport type * as testing from \"../testing.js\";\nimport type * as util_FastIntegerCompression from \"../util/FastIntegerCompression.js\";\nimport type * as util_assertNever from \"../util/assertNever.js\";\nimport type * as util_asyncMap from \"../util/asyncMap.js\";\nimport type * as util_compression from \"../util/compression.js\";\nimport type * as util_geometry from \"../util/geometry.js\";\nimport type * as util_isSimpleObject from \"../util/isSimpleObject.js\";\nimport type * as util_llm from \"../util/llm.js\";\nimport type * as util_minheap from \"../util/minheap.js\";\nimport type * as util_object from \"../util/object.js\";\nimport type * as util_sleep from \"../util/sleep.js\";\nimport type * as util_types from \"../util/types.js\";\nimport type * as util_xxhash from \"../util/xxhash.js\";\nimport type * as world from \"../world.js\";\n\n/**\n * A utility for referencing Convex functions in your app's API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = api.myModule.myFunction;\n * ```\n */\ndeclare const fullApi: ApiFromModules<{\n  \"agent/conversation\": typeof agent_conversation;\n  \"agent/embeddingsCache\": typeof agent_embeddingsCache;\n  \"agent/memory\": typeof agent_memory;\n  \"aiTown/agent\": typeof aiTown_agent;\n  \"aiTown/agentDescription\": typeof aiTown_agentDescription;\n  \"aiTown/agentInputs\": typeof aiTown_agentInputs;\n  \"aiTown/agentOperations\": typeof aiTown_agentOperations;\n  \"aiTown/conversation\": typeof aiTown_conversation;\n  \"aiTown/conversationMembership\": typeof aiTown_conversationMembership;\n  \"aiTown/game\": typeof aiTown_game;\n  \"aiTown/ids\": typeof aiTown_ids;\n  \"aiTown/inputHandler\": typeof aiTown_inputHandler;\n  \"aiTown/inputs\": typeof aiTown_inputs;\n  \"aiTown/insertInput\": typeof aiTown_insertInput;\n  \"aiTown/location\": typeof aiTown_location;\n  \"aiTown/main\": typeof aiTown_main;\n  \"aiTown/movement\": typeof aiTown_movement;\n  \"aiTown/player\": typeof aiTown_player;\n  \"aiTown/playerDescription\": typeof aiTown_playerDescription;\n  \"aiTown/world\": typeof aiTown_world;\n  \"aiTown/worldMap\": typeof aiTown_worldMap;\n  constants: typeof constants;\n  crons: typeof crons;\n  \"engine/abstractGame\": typeof engine_abstractGame;\n  \"engine/historicalObject\": typeof engine_historicalObject;\n  http: typeof http;\n  init: typeof init;\n  messages: typeof messages;\n  music: typeof music;\n  testing: typeof testing;\n  \"util/FastIntegerCompression\": typeof util_FastIntegerCompression;\n  \"util/assertNever\": typeof util_assertNever;\n  \"util/asyncMap\": typeof util_asyncMap;\n  \"util/compression\": typeof util_compression;\n  \"util/geometry\": typeof util_geometry;\n  \"util/isSimpleObject\": typeof util_isSimpleObject;\n  \"util/llm\": typeof util_llm;\n  \"util/minheap\": typeof util_minheap;\n  \"util/object\": typeof util_object;\n  \"util/sleep\": typeof util_sleep;\n  \"util/types\": typeof util_types;\n  \"util/xxhash\": typeof util_xxhash;\n  world: typeof world;\n}>;\nexport declare const api: FilterApi<\n  typeof fullApi,\n  FunctionReference<any, \"public\">\n>;\nexport declare const internal: FilterApi<\n  typeof fullApi,\n  FunctionReference<any, \"internal\">\n>;\n"
  },
  {
    "path": "convex/_generated/api.js",
    "content": "/* eslint-disable */\n/**\n * Generated `api` utility.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport { anyApi } from \"convex/server\";\n\n/**\n * A utility for referencing Convex functions in your app's API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = api.myModule.myFunction;\n * ```\n */\nexport const api = anyApi;\nexport const internal = anyApi;\n"
  },
  {
    "path": "convex/_generated/dataModel.d.ts",
    "content": "/* eslint-disable */\n/**\n * Generated data model types.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport type {\n  DataModelFromSchemaDefinition,\n  DocumentByName,\n  TableNamesInDataModel,\n  SystemTableNames,\n} from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\nimport schema from \"../schema.js\";\n\n/**\n * The names of all of your Convex tables.\n */\nexport type TableNames = TableNamesInDataModel<DataModel>;\n\n/**\n * The type of a document stored in Convex.\n *\n * @typeParam TableName - A string literal type of the table name (like \"users\").\n */\nexport type Doc<TableName extends TableNames> = DocumentByName<\n  DataModel,\n  TableName\n>;\n\n/**\n * An identifier for a document in Convex.\n *\n * Convex documents are uniquely identified by their `Id`, which is accessible\n * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).\n *\n * Documents can be loaded using `db.get(id)` in query and mutation functions.\n *\n * IDs are just strings at runtime, but this type can be used to distinguish them from other\n * strings when type checking.\n *\n * @typeParam TableName - A string literal type of the table name (like \"users\").\n */\nexport type Id<TableName extends TableNames | SystemTableNames> =\n  GenericId<TableName>;\n\n/**\n * A type describing your Convex data model.\n *\n * This type includes information about what tables you have, the type of\n * documents stored in those tables, and the indexes defined on them.\n *\n * This type is used to parameterize methods like `queryGeneric` and\n * `mutationGeneric` to make them type-safe.\n */\nexport type DataModel = DataModelFromSchemaDefinition<typeof schema>;\n"
  },
  {
    "path": "convex/_generated/server.d.ts",
    "content": "/* eslint-disable */\n/**\n * Generated utilities for implementing server-side Convex query and mutation functions.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport {\n  ActionBuilder,\n  HttpActionBuilder,\n  MutationBuilder,\n  QueryBuilder,\n  GenericActionCtx,\n  GenericMutationCtx,\n  GenericQueryCtx,\n  GenericDatabaseReader,\n  GenericDatabaseWriter,\n} from \"convex/server\";\nimport type { DataModel } from \"./dataModel.js\";\n\n/**\n * Define a query in this Convex app's public API.\n *\n * This function will be allowed to read your Convex database and will be accessible from the client.\n *\n * @param func - The query function. It receives a {@link QueryCtx} as its first argument.\n * @returns The wrapped query. Include this as an `export` to name it and make it accessible.\n */\nexport declare const query: QueryBuilder<DataModel, \"public\">;\n\n/**\n * Define a query that is only accessible from other Convex functions (but not from the client).\n *\n * This function will be allowed to read from your Convex database. It will not be accessible from the client.\n *\n * @param func - The query function. It receives a {@link QueryCtx} as its first argument.\n * @returns The wrapped query. Include this as an `export` to name it and make it accessible.\n */\nexport declare const internalQuery: QueryBuilder<DataModel, \"internal\">;\n\n/**\n * Define a mutation in this Convex app's public API.\n *\n * This function will be allowed to modify your Convex database and will be accessible from the client.\n *\n * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.\n * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.\n */\nexport declare const mutation: MutationBuilder<DataModel, \"public\">;\n\n/**\n * Define a mutation that is only accessible from other Convex functions (but not from the client).\n *\n * This function will be allowed to modify your Convex database. It will not be accessible from the client.\n *\n * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.\n * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.\n */\nexport declare const internalMutation: MutationBuilder<DataModel, \"internal\">;\n\n/**\n * Define an action in this Convex app's public API.\n *\n * An action is a function which can execute any JavaScript code, including non-deterministic\n * code and code with side-effects, like calling third-party services.\n * They can be run in Convex's JavaScript environment or in Node.js using the \"use node\" directive.\n * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.\n *\n * @param func - The action. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped action. Include this as an `export` to name it and make it accessible.\n */\nexport declare const action: ActionBuilder<DataModel, \"public\">;\n\n/**\n * Define an action that is only accessible from other Convex functions (but not from the client).\n *\n * @param func - The function. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped function. Include this as an `export` to name it and make it accessible.\n */\nexport declare const internalAction: ActionBuilder<DataModel, \"internal\">;\n\n/**\n * Define an HTTP action.\n *\n * This function will be used to respond to HTTP requests received by a Convex\n * deployment if the requests matches the path and method where this action\n * is routed. Be sure to route your action in `convex/http.js`.\n *\n * @param func - The function. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.\n */\nexport declare const httpAction: HttpActionBuilder;\n\n/**\n * A set of services for use within Convex query functions.\n *\n * The query context is passed as the first argument to any Convex query\n * function run on the server.\n *\n * This differs from the {@link MutationCtx} because all of the services are\n * read-only.\n */\nexport type QueryCtx = GenericQueryCtx<DataModel>;\n\n/**\n * A set of services for use within Convex mutation functions.\n *\n * The mutation context is passed as the first argument to any Convex mutation\n * function run on the server.\n */\nexport type MutationCtx = GenericMutationCtx<DataModel>;\n\n/**\n * A set of services for use within Convex action functions.\n *\n * The action context is passed as the first argument to any Convex action\n * function run on the server.\n */\nexport type ActionCtx = GenericActionCtx<DataModel>;\n\n/**\n * An interface to read from the database within Convex query functions.\n *\n * The two entry points are {@link DatabaseReader.get}, which fetches a single\n * document by its {@link Id}, or {@link DatabaseReader.query}, which starts\n * building a query.\n */\nexport type DatabaseReader = GenericDatabaseReader<DataModel>;\n\n/**\n * An interface to read from and write to the database within Convex mutation\n * functions.\n *\n * Convex guarantees that all writes within a single mutation are\n * executed atomically, so you never have to worry about partial writes leaving\n * your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control)\n * for the guarantees Convex provides your functions.\n */\nexport type DatabaseWriter = GenericDatabaseWriter<DataModel>;\n"
  },
  {
    "path": "convex/_generated/server.js",
    "content": "/* eslint-disable */\n/**\n * Generated utilities for implementing server-side Convex query and mutation functions.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport {\n  actionGeneric,\n  httpActionGeneric,\n  queryGeneric,\n  mutationGeneric,\n  internalActionGeneric,\n  internalMutationGeneric,\n  internalQueryGeneric,\n} from \"convex/server\";\n\n/**\n * Define a query in this Convex app's public API.\n *\n * This function will be allowed to read your Convex database and will be accessible from the client.\n *\n * @param func - The query function. It receives a {@link QueryCtx} as its first argument.\n * @returns The wrapped query. Include this as an `export` to name it and make it accessible.\n */\nexport const query = queryGeneric;\n\n/**\n * Define a query that is only accessible from other Convex functions (but not from the client).\n *\n * This function will be allowed to read from your Convex database. It will not be accessible from the client.\n *\n * @param func - The query function. It receives a {@link QueryCtx} as its first argument.\n * @returns The wrapped query. Include this as an `export` to name it and make it accessible.\n */\nexport const internalQuery = internalQueryGeneric;\n\n/**\n * Define a mutation in this Convex app's public API.\n *\n * This function will be allowed to modify your Convex database and will be accessible from the client.\n *\n * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.\n * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.\n */\nexport const mutation = mutationGeneric;\n\n/**\n * Define a mutation that is only accessible from other Convex functions (but not from the client).\n *\n * This function will be allowed to modify your Convex database. It will not be accessible from the client.\n *\n * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.\n * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.\n */\nexport const internalMutation = internalMutationGeneric;\n\n/**\n * Define an action in this Convex app's public API.\n *\n * An action is a function which can execute any JavaScript code, including non-deterministic\n * code and code with side-effects, like calling third-party services.\n * They can be run in Convex's JavaScript environment or in Node.js using the \"use node\" directive.\n * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.\n *\n * @param func - The action. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped action. Include this as an `export` to name it and make it accessible.\n */\nexport const action = actionGeneric;\n\n/**\n * Define an action that is only accessible from other Convex functions (but not from the client).\n *\n * @param func - The function. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped function. Include this as an `export` to name it and make it accessible.\n */\nexport const internalAction = internalActionGeneric;\n\n/**\n * Define a Convex HTTP action.\n *\n * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object\n * as its second.\n * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.\n */\nexport const httpAction = httpActionGeneric;\n"
  },
  {
    "path": "convex/agent/conversation.ts",
    "content": "import { v } from 'convex/values';\nimport { Id } from '../_generated/dataModel';\nimport { ActionCtx, internalQuery } from '../_generated/server';\nimport { LLMMessage, chatCompletion } from '../util/llm';\nimport * as memory from './memory';\nimport { api, internal } from '../_generated/api';\nimport * as embeddingsCache from './embeddingsCache';\nimport { GameId, conversationId, playerId } from '../aiTown/ids';\nimport { NUM_MEMORIES_TO_SEARCH } from '../constants';\n\nconst selfInternal = internal.agent.conversation;\n\nexport async function startConversationMessage(\n  ctx: ActionCtx,\n  worldId: Id<'worlds'>,\n  conversationId: GameId<'conversations'>,\n  playerId: GameId<'players'>,\n  otherPlayerId: GameId<'players'>,\n): Promise<string> {\n  const { player, otherPlayer, agent, otherAgent, lastConversation } = await ctx.runQuery(\n    selfInternal.queryPromptData,\n    {\n      worldId,\n      playerId,\n      otherPlayerId,\n      conversationId,\n    },\n  );\n  const embedding = await embeddingsCache.fetch(\n    ctx,\n    `${player.name} is talking to ${otherPlayer.name}`,\n  );\n\n  const memories = await memory.searchMemories(\n    ctx,\n    player.id as GameId<'players'>,\n    embedding,\n    Number(process.env.NUM_MEMORIES_TO_SEARCH) || NUM_MEMORIES_TO_SEARCH,\n  );\n\n  const memoryWithOtherPlayer = memories.find(\n    (m) => m.data.type === 'conversation' && m.data.playerIds.includes(otherPlayerId),\n  );\n  const prompt = [\n    `You are ${player.name}, and you just started a conversation with ${otherPlayer.name}.`,\n  ];\n  prompt.push(...agentPrompts(otherPlayer, agent, otherAgent ?? null));\n  prompt.push(...previousConversationPrompt(otherPlayer, lastConversation));\n  prompt.push(...relatedMemoriesPrompt(memories));\n  if (memoryWithOtherPlayer) {\n    prompt.push(\n      `Be sure to include some detail or question about a previous conversation in your greeting.`,\n    );\n  }\n  const lastPrompt = `${player.name} to ${otherPlayer.name}:`;\n  prompt.push(lastPrompt);\n\n  const { content } = await chatCompletion({\n    messages: [\n      {\n        role: 'system',\n        content: prompt.join('\\n'),\n      },\n    ],\n    max_tokens: 300,\n    stop: stopWords(otherPlayer.name, player.name),\n  });\n  return trimContentPrefx(content, lastPrompt);\n}\n\nfunction trimContentPrefx(content: string, prompt: string) {\n  if (content.startsWith(prompt)) {\n    return content.slice(prompt.length).trim();\n  }\n  return content;\n}\n\nexport async function continueConversationMessage(\n  ctx: ActionCtx,\n  worldId: Id<'worlds'>,\n  conversationId: GameId<'conversations'>,\n  playerId: GameId<'players'>,\n  otherPlayerId: GameId<'players'>,\n): Promise<string> {\n  const { player, otherPlayer, conversation, agent, otherAgent } = await ctx.runQuery(\n    selfInternal.queryPromptData,\n    {\n      worldId,\n      playerId,\n      otherPlayerId,\n      conversationId,\n    },\n  );\n  const now = Date.now();\n  const started = new Date(conversation.created);\n  const embedding = await embeddingsCache.fetch(\n    ctx,\n    `What do you think about ${otherPlayer.name}?`,\n  );\n  const memories = await memory.searchMemories(ctx, player.id as GameId<'players'>, embedding, 3);\n  const prompt = [\n    `You are ${player.name}, and you're currently in a conversation with ${otherPlayer.name}.`,\n    `The conversation started at ${started.toLocaleString()}. It's now ${now.toLocaleString()}.`,\n  ];\n  prompt.push(...agentPrompts(otherPlayer, agent, otherAgent ?? null));\n  prompt.push(...relatedMemoriesPrompt(memories));\n  prompt.push(\n    `Below is the current chat history between you and ${otherPlayer.name}.`,\n    `DO NOT greet them again. Do NOT use the word \"Hey\" too often. Your response should be brief and within 200 characters.`,\n  );\n\n  const llmMessages: LLMMessage[] = [\n    {\n      role: 'system',\n      content: prompt.join('\\n'),\n    },\n    ...(await previousMessages(\n      ctx,\n      worldId,\n      player,\n      otherPlayer,\n      conversation.id as GameId<'conversations'>,\n    )),\n  ];\n  const lastPrompt = `${player.name} to ${otherPlayer.name}:`;\n  llmMessages.push({ role: 'user', content: lastPrompt });\n\n  const { content } = await chatCompletion({\n    messages: llmMessages,\n    max_tokens: 300,\n    stop: stopWords(otherPlayer.name, player.name),\n  });\n  return trimContentPrefx(content, lastPrompt);\n}\n\nexport async function leaveConversationMessage(\n  ctx: ActionCtx,\n  worldId: Id<'worlds'>,\n  conversationId: GameId<'conversations'>,\n  playerId: GameId<'players'>,\n  otherPlayerId: GameId<'players'>,\n): Promise<string> {\n  const { player, otherPlayer, conversation, agent, otherAgent } = await ctx.runQuery(\n    selfInternal.queryPromptData,\n    {\n      worldId,\n      playerId,\n      otherPlayerId,\n      conversationId,\n    },\n  );\n  const prompt = [\n    `You are ${player.name}, and you're currently in a conversation with ${otherPlayer.name}.`,\n    `You've decided to leave the question and would like to politely tell them you're leaving the conversation.`,\n  ];\n  prompt.push(...agentPrompts(otherPlayer, agent, otherAgent ?? null));\n  prompt.push(\n    `Below is the current chat history between you and ${otherPlayer.name}.`,\n    `How would you like to tell them that you're leaving? Your response should be brief and within 200 characters.`,\n  );\n  const llmMessages: LLMMessage[] = [\n    {\n      role: 'system',\n      content: prompt.join('\\n'),\n    },\n    ...(await previousMessages(\n      ctx,\n      worldId,\n      player,\n      otherPlayer,\n      conversation.id as GameId<'conversations'>,\n    )),\n  ];\n  const lastPrompt = `${player.name} to ${otherPlayer.name}:`;\n  llmMessages.push({ role: 'user', content: lastPrompt });\n\n  const { content } = await chatCompletion({\n    messages: llmMessages,\n    max_tokens: 300,\n    stop: stopWords(otherPlayer.name, player.name),\n  });\n  return trimContentPrefx(content, lastPrompt);\n}\n\nfunction agentPrompts(\n  otherPlayer: { name: string },\n  agent: { identity: string; plan: string } | null,\n  otherAgent: { identity: string; plan: string } | null,\n): string[] {\n  const prompt = [];\n  if (agent) {\n    prompt.push(`About you: ${agent.identity}`);\n    prompt.push(`Your goals for the conversation: ${agent.plan}`);\n  }\n  if (otherAgent) {\n    prompt.push(`About ${otherPlayer.name}: ${otherAgent.identity}`);\n  }\n  return prompt;\n}\n\nfunction previousConversationPrompt(\n  otherPlayer: { name: string },\n  conversation: { created: number } | null,\n): string[] {\n  const prompt = [];\n  if (conversation) {\n    const prev = new Date(conversation.created);\n    const now = new Date();\n    prompt.push(\n      `Last time you chatted with ${\n        otherPlayer.name\n      } it was ${prev.toLocaleString()}. It's now ${now.toLocaleString()}.`,\n    );\n  }\n  return prompt;\n}\n\nfunction relatedMemoriesPrompt(memories: memory.Memory[]): string[] {\n  const prompt = [];\n  if (memories.length > 0) {\n    prompt.push(`Here are some related memories in decreasing relevance order:`);\n    for (const memory of memories) {\n      prompt.push(' - ' + memory.description);\n    }\n  }\n  return prompt;\n}\n\nasync function previousMessages(\n  ctx: ActionCtx,\n  worldId: Id<'worlds'>,\n  player: { id: string; name: string },\n  otherPlayer: { id: string; name: string },\n  conversationId: GameId<'conversations'>,\n) {\n  const llmMessages: LLMMessage[] = [];\n  const prevMessages = await ctx.runQuery(api.messages.listMessages, { worldId, conversationId });\n  for (const message of prevMessages) {\n    const author = message.author === player.id ? player : otherPlayer;\n    const recipient = message.author === player.id ? otherPlayer : player;\n    llmMessages.push({\n      role: 'user',\n      content: `${author.name} to ${recipient.name}: ${message.text}`,\n    });\n  }\n  return llmMessages;\n}\n\nexport const queryPromptData = internalQuery({\n  args: {\n    worldId: v.id('worlds'),\n    playerId,\n    otherPlayerId: playerId,\n    conversationId,\n  },\n  handler: async (ctx, args) => {\n    const world = await ctx.db.get(args.worldId);\n    if (!world) {\n      throw new Error(`World ${args.worldId} not found`);\n    }\n    const player = world.players.find((p) => p.id === args.playerId);\n    if (!player) {\n      throw new Error(`Player ${args.playerId} not found`);\n    }\n    const playerDescription = await ctx.db\n      .query('playerDescriptions')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', args.playerId))\n      .first();\n    if (!playerDescription) {\n      throw new Error(`Player description for ${args.playerId} not found`);\n    }\n    const otherPlayer = world.players.find((p) => p.id === args.otherPlayerId);\n    if (!otherPlayer) {\n      throw new Error(`Player ${args.otherPlayerId} not found`);\n    }\n    const otherPlayerDescription = await ctx.db\n      .query('playerDescriptions')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', args.otherPlayerId))\n      .first();\n    if (!otherPlayerDescription) {\n      throw new Error(`Player description for ${args.otherPlayerId} not found`);\n    }\n    const conversation = world.conversations.find((c) => c.id === args.conversationId);\n    if (!conversation) {\n      throw new Error(`Conversation ${args.conversationId} not found`);\n    }\n    const agent = world.agents.find((a) => a.playerId === args.playerId);\n    if (!agent) {\n      throw new Error(`Player ${args.playerId} not found`);\n    }\n    const agentDescription = await ctx.db\n      .query('agentDescriptions')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('agentId', agent.id))\n      .first();\n    if (!agentDescription) {\n      throw new Error(`Agent description for ${agent.id} not found`);\n    }\n    const otherAgent = world.agents.find((a) => a.playerId === args.otherPlayerId);\n    let otherAgentDescription;\n    if (otherAgent) {\n      otherAgentDescription = await ctx.db\n        .query('agentDescriptions')\n        .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('agentId', otherAgent.id))\n        .first();\n      if (!otherAgentDescription) {\n        throw new Error(`Agent description for ${otherAgent.id} not found`);\n      }\n    }\n    const lastTogether = await ctx.db\n      .query('participatedTogether')\n      .withIndex('edge', (q) =>\n        q\n          .eq('worldId', args.worldId)\n          .eq('player1', args.playerId)\n          .eq('player2', args.otherPlayerId),\n      )\n      // Order by conversation end time descending.\n      .order('desc')\n      .first();\n\n    let lastConversation = null;\n    if (lastTogether) {\n      lastConversation = await ctx.db\n        .query('archivedConversations')\n        .withIndex('worldId', (q) =>\n          q.eq('worldId', args.worldId).eq('id', lastTogether.conversationId),\n        )\n        .first();\n      if (!lastConversation) {\n        throw new Error(`Conversation ${lastTogether.conversationId} not found`);\n      }\n    }\n    return {\n      player: { name: playerDescription.name, ...player },\n      otherPlayer: { name: otherPlayerDescription.name, ...otherPlayer },\n      conversation,\n      agent: { identity: agentDescription.identity, plan: agentDescription.plan, ...agent },\n      otherAgent: otherAgent && {\n        identity: otherAgentDescription!.identity,\n        plan: otherAgentDescription!.plan,\n        ...otherAgent,\n      },\n      lastConversation,\n    };\n  },\n});\n\nfunction stopWords(otherPlayer: string, player: string) {\n  // These are the words we ask the LLM to stop on. OpenAI only supports 4.\n  const variants = [`${otherPlayer} to ${player}`];\n  return variants.flatMap((stop) => [stop + ':', stop.toLowerCase() + ':']);\n}\n"
  },
  {
    "path": "convex/agent/embeddingsCache.ts",
    "content": "import { v } from 'convex/values';\nimport { ActionCtx, internalMutation, internalQuery } from '../_generated/server';\nimport { internal } from '../_generated/api';\nimport { Id } from '../_generated/dataModel';\nimport { fetchEmbeddingBatch } from '../util/llm';\n\nconst selfInternal = internal.agent.embeddingsCache;\n\nexport async function fetch(ctx: ActionCtx, text: string) {\n  const result = await fetchBatch(ctx, [text]);\n  return result.embeddings[0];\n}\n\nexport async function fetchBatch(ctx: ActionCtx, texts: string[]) {\n  const start = Date.now();\n\n  const textHashes = await Promise.all(texts.map((text) => hashText(text)));\n  const results = new Array<number[]>(texts.length);\n  const cacheResults = await ctx.runQuery(selfInternal.getEmbeddingsByText, {\n    textHashes,\n  });\n  for (const { index, embedding } of cacheResults) {\n    results[index] = embedding;\n  }\n  const toWrite = [];\n  if (cacheResults.length < texts.length) {\n    const missingIndexes = [...results.keys()].filter((i) => !results[i]);\n    const missingTexts = missingIndexes.map((i) => texts[i]);\n    const response = await fetchEmbeddingBatch(missingTexts);\n    if (response.embeddings.length !== missingIndexes.length) {\n      throw new Error(\n        `Expected ${missingIndexes.length} embeddings, got ${response.embeddings.length}`,\n      );\n    }\n    for (let i = 0; i < missingIndexes.length; i++) {\n      const resultIndex = missingIndexes[i];\n      toWrite.push({\n        textHash: textHashes[resultIndex],\n        embedding: response.embeddings[i],\n      });\n      results[resultIndex] = response.embeddings[i];\n    }\n  }\n  if (toWrite.length > 0) {\n    await ctx.runMutation(selfInternal.writeEmbeddings, { embeddings: toWrite });\n  }\n  return {\n    embeddings: results,\n    hits: cacheResults.length,\n    ms: Date.now() - start,\n  };\n}\n\nasync function hashText(text: string) {\n  const textEncoder = new TextEncoder();\n  const buf = textEncoder.encode(text);\n  if (typeof crypto === 'undefined') {\n    // Ugly, ugly hax to get ESBuild to not try to bundle this node dependency.\n    const f = () => 'node:crypto';\n    const crypto = (await import(f())) as typeof import('crypto');\n    const hash = crypto.createHash('sha256');\n    hash.update(buf);\n    return hash.digest().buffer;\n  } else {\n    return await crypto.subtle.digest('SHA-256', buf);\n  }\n}\n\nexport const getEmbeddingsByText = internalQuery({\n  args: { textHashes: v.array(v.bytes()) },\n  handler: async (\n    ctx,\n    args,\n  ): Promise<{ index: number; embeddingId: Id<'embeddingsCache'>; embedding: number[] }[]> => {\n    const out = [];\n    for (let i = 0; i < args.textHashes.length; i++) {\n      const textHash = args.textHashes[i];\n      const result = await ctx.db\n        .query('embeddingsCache')\n        .withIndex('text', (q) => q.eq('textHash', textHash))\n        .first();\n      if (result) {\n        out.push({\n          index: i,\n          embeddingId: result._id,\n          embedding: result.embedding,\n        });\n      }\n    }\n    return out;\n  },\n});\n\nexport const writeEmbeddings = internalMutation({\n  args: {\n    embeddings: v.array(\n      v.object({\n        textHash: v.bytes(),\n        embedding: v.array(v.float64()),\n      }),\n    ),\n  },\n  handler: async (ctx, args): Promise<Id<'embeddingsCache'>[]> => {\n    const ids = [];\n    for (const embedding of args.embeddings) {\n      ids.push(await ctx.db.insert('embeddingsCache', embedding));\n    }\n    return ids;\n  },\n});\n"
  },
  {
    "path": "convex/agent/memory.ts",
    "content": "import { v } from 'convex/values';\nimport { ActionCtx, DatabaseReader, internalMutation, internalQuery } from '../_generated/server';\nimport { Doc, Id } from '../_generated/dataModel';\nimport { internal } from '../_generated/api';\nimport { LLMMessage, chatCompletion, fetchEmbedding } from '../util/llm';\nimport { asyncMap } from '../util/asyncMap';\nimport { GameId, agentId, conversationId, playerId } from '../aiTown/ids';\nimport { SerializedPlayer } from '../aiTown/player';\nimport { memoryFields } from './schema';\n\n// How long to wait before updating a memory's last access time.\nexport const MEMORY_ACCESS_THROTTLE = 300_000; // In ms\n// We fetch 10x the number of memories by relevance, to have more candidates\n// for sorting by relevance + recency + importance.\nconst MEMORY_OVERFETCH = 10;\nconst selfInternal = internal.agent.memory;\n\nexport type Memory = Doc<'memories'>;\nexport type MemoryType = Memory['data']['type'];\nexport type MemoryOfType<T extends MemoryType> = Omit<Memory, 'data'> & {\n  data: Extract<Memory['data'], { type: T }>;\n};\n\nexport async function rememberConversation(\n  ctx: ActionCtx,\n  worldId: Id<'worlds'>,\n  agentId: GameId<'agents'>,\n  playerId: GameId<'players'>,\n  conversationId: GameId<'conversations'>,\n) {\n  const data = await ctx.runQuery(selfInternal.loadConversation, {\n    worldId,\n    playerId,\n    conversationId,\n  });\n  const { player, otherPlayer } = data;\n  const messages = await ctx.runQuery(selfInternal.loadMessages, { worldId, conversationId });\n  if (!messages.length) {\n    return;\n  }\n\n  const llmMessages: LLMMessage[] = [\n    {\n      role: 'user',\n      content: `You are ${player.name}, and you just finished a conversation with ${otherPlayer.name}. I would\n      like you to summarize the conversation from ${player.name}'s perspective, using first-person pronouns like\n      \"I,\" and add if you liked or disliked this interaction.`,\n    },\n  ];\n  const authors = new Set<GameId<'players'>>();\n  for (const message of messages) {\n    const author = message.author === player.id ? player : otherPlayer;\n    authors.add(author.id as GameId<'players'>);\n    const recipient = message.author === player.id ? otherPlayer : player;\n    llmMessages.push({\n      role: 'user',\n      content: `${author.name} to ${recipient.name}: ${message.text}`,\n    });\n  }\n  llmMessages.push({ role: 'user', content: 'Summary:' });\n  const { content } = await chatCompletion({\n    messages: llmMessages,\n    max_tokens: 500,\n  });\n  const description = `Conversation with ${otherPlayer.name} at ${new Date(\n    data.conversation._creationTime,\n  ).toLocaleString()}: ${content}`;\n  const importance = await calculateImportance(description);\n  const { embedding } = await fetchEmbedding(description);\n  authors.delete(player.id as GameId<'players'>);\n  await ctx.runMutation(selfInternal.insertMemory, {\n    agentId,\n    playerId: player.id,\n    description,\n    importance,\n    lastAccess: messages[messages.length - 1]._creationTime,\n    data: {\n      type: 'conversation',\n      conversationId,\n      playerIds: [...authors],\n    },\n    embedding,\n  });\n  await reflectOnMemories(ctx, worldId, playerId);\n  return description;\n}\n\nexport const loadConversation = internalQuery({\n  args: {\n    worldId: v.id('worlds'),\n    playerId,\n    conversationId,\n  },\n  handler: async (ctx, args) => {\n    const world = await ctx.db.get(args.worldId);\n    if (!world) {\n      throw new Error(`World ${args.worldId} not found`);\n    }\n    const player = world.players.find((p) => p.id === args.playerId);\n    if (!player) {\n      throw new Error(`Player ${args.playerId} not found`);\n    }\n    const playerDescription = await ctx.db\n      .query('playerDescriptions')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', args.playerId))\n      .first();\n    if (!playerDescription) {\n      throw new Error(`Player description for ${args.playerId} not found`);\n    }\n    const conversation = await ctx.db\n      .query('archivedConversations')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('id', args.conversationId))\n      .first();\n    if (!conversation) {\n      throw new Error(`Conversation ${args.conversationId} not found`);\n    }\n    const otherParticipator = await ctx.db\n      .query('participatedTogether')\n      .withIndex('conversation', (q) =>\n        q\n          .eq('worldId', args.worldId)\n          .eq('player1', args.playerId)\n          .eq('conversationId', args.conversationId),\n      )\n      .first();\n    if (!otherParticipator) {\n      throw new Error(\n        `Couldn't find other participant in conversation ${args.conversationId} with player ${args.playerId}`,\n      );\n    }\n    const otherPlayerId = otherParticipator.player2;\n    let otherPlayer: SerializedPlayer | Doc<'archivedPlayers'> | null =\n      world.players.find((p) => p.id === otherPlayerId) ?? null;\n    if (!otherPlayer) {\n      otherPlayer = await ctx.db\n        .query('archivedPlayers')\n        .withIndex('worldId', (q) => q.eq('worldId', world._id).eq('id', otherPlayerId))\n        .first();\n    }\n    if (!otherPlayer) {\n      throw new Error(`Conversation ${args.conversationId} other player not found`);\n    }\n    const otherPlayerDescription = await ctx.db\n      .query('playerDescriptions')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', otherPlayerId))\n      .first();\n    if (!otherPlayerDescription) {\n      throw new Error(`Player description for ${otherPlayerId} not found`);\n    }\n    return {\n      player: { ...player, name: playerDescription.name },\n      conversation,\n      otherPlayer: { ...otherPlayer, name: otherPlayerDescription.name },\n    };\n  },\n});\n\nexport async function searchMemories(\n  ctx: ActionCtx,\n  playerId: GameId<'players'>,\n  searchEmbedding: number[],\n  n: number = 3,\n) {\n  const candidates = await ctx.vectorSearch('memoryEmbeddings', 'embedding', {\n    vector: searchEmbedding,\n    filter: (q) => q.eq('playerId', playerId),\n    limit: n * MEMORY_OVERFETCH,\n  });\n  const rankedMemories = await ctx.runMutation(selfInternal.rankAndTouchMemories, {\n    candidates,\n    n,\n  });\n  return rankedMemories.map(({ memory }) => memory);\n}\n\nfunction makeRange(values: number[]) {\n  const min = Math.min(...values);\n  const max = Math.max(...values);\n  return [min, max] as const;\n}\n\nfunction normalize(value: number, range: readonly [number, number]) {\n  const [min, max] = range;\n  return (value - min) / (max - min);\n}\n\nexport const rankAndTouchMemories = internalMutation({\n  args: {\n    candidates: v.array(v.object({ _id: v.id('memoryEmbeddings'), _score: v.number() })),\n    n: v.number(),\n  },\n  handler: async (ctx, args) => {\n    const ts = Date.now();\n    const relatedMemories = await asyncMap(args.candidates, async ({ _id }) => {\n      const memory = await ctx.db\n        .query('memories')\n        .withIndex('embeddingId', (q) => q.eq('embeddingId', _id))\n        .first();\n      if (!memory) throw new Error(`Memory for embedding ${_id} not found`);\n      return memory;\n    });\n\n    // TODO: fetch <count> recent memories and <count> important memories\n    // so we don't miss them in case they were a little less relevant.\n    const recencyScore = relatedMemories.map((memory) => {\n      const hoursSinceAccess = (ts - memory.lastAccess) / 1000 / 60 / 60;\n      return 0.99 ** Math.floor(hoursSinceAccess);\n    });\n    const relevanceRange = makeRange(args.candidates.map((c) => c._score));\n    const importanceRange = makeRange(relatedMemories.map((m) => m.importance));\n    const recencyRange = makeRange(recencyScore);\n    const memoryScores = relatedMemories.map((memory, idx) => ({\n      memory,\n      overallScore:\n        normalize(args.candidates[idx]._score, relevanceRange) +\n        normalize(memory.importance, importanceRange) +\n        normalize(recencyScore[idx], recencyRange),\n    }));\n    memoryScores.sort((a, b) => b.overallScore - a.overallScore);\n    const accessed = memoryScores.slice(0, args.n);\n    await asyncMap(accessed, async ({ memory }) => {\n      if (memory.lastAccess < ts - MEMORY_ACCESS_THROTTLE) {\n        await ctx.db.patch(memory._id, { lastAccess: ts });\n      }\n    });\n    return accessed;\n  },\n});\n\nexport const loadMessages = internalQuery({\n  args: {\n    worldId: v.id('worlds'),\n    conversationId,\n  },\n  handler: async (ctx, args): Promise<Doc<'messages'>[]> => {\n    const messages = await ctx.db\n      .query('messages')\n      .withIndex('conversationId', (q) =>\n        q.eq('worldId', args.worldId).eq('conversationId', args.conversationId),\n      )\n      .collect();\n    return messages;\n  },\n});\n\nasync function calculateImportance(description: string) {\n  const { content: importanceRaw } = await chatCompletion({\n    messages: [\n      {\n        role: 'user',\n        content: `On the scale of 0 to 9, where 0 is purely mundane (e.g., brushing teeth, making bed) and 9 is extremely poignant (e.g., a break up, college acceptance), rate the likely poignancy of the following piece of memory.\n      Memory: ${description}\n      Answer on a scale of 0 to 9. Respond with number only, e.g. \"5\"`,\n      },\n    ],\n    temperature: 0.0,\n    max_tokens: 1,\n  });\n\n  let importance = parseFloat(importanceRaw);\n  if (isNaN(importance)) {\n    importance = +(importanceRaw.match(/\\d+/)?.[0] ?? NaN);\n  }\n  if (isNaN(importance)) {\n    console.debug('Could not parse memory importance from: ', importanceRaw);\n    importance = 5;\n  }\n  return importance;\n}\n\nconst { embeddingId: _embeddingId, ...memoryFieldsWithoutEmbeddingId } = memoryFields;\n\nexport const insertMemory = internalMutation({\n  args: {\n    agentId,\n    embedding: v.array(v.float64()),\n    ...memoryFieldsWithoutEmbeddingId,\n  },\n  handler: async (ctx, { agentId: _, embedding, ...memory }): Promise<void> => {\n    const embeddingId = await ctx.db.insert('memoryEmbeddings', {\n      playerId: memory.playerId,\n      embedding,\n    });\n    await ctx.db.insert('memories', {\n      ...memory,\n      embeddingId,\n    });\n  },\n});\n\nexport const insertReflectionMemories = internalMutation({\n  args: {\n    worldId: v.id('worlds'),\n    playerId,\n    reflections: v.array(\n      v.object({\n        description: v.string(),\n        relatedMemoryIds: v.array(v.id('memories')),\n        importance: v.number(),\n        embedding: v.array(v.float64()),\n      }),\n    ),\n  },\n  handler: async (ctx, { playerId, reflections }) => {\n    const lastAccess = Date.now();\n    for (const { embedding, relatedMemoryIds, ...rest } of reflections) {\n      const embeddingId = await ctx.db.insert('memoryEmbeddings', {\n        playerId,\n        embedding,\n      });\n      await ctx.db.insert('memories', {\n        playerId,\n        embeddingId,\n        lastAccess,\n        ...rest,\n        data: {\n          type: 'reflection',\n          relatedMemoryIds,\n        },\n      });\n    }\n  },\n});\n\nasync function reflectOnMemories(\n  ctx: ActionCtx,\n  worldId: Id<'worlds'>,\n  playerId: GameId<'players'>,\n) {\n  const { memories, lastReflectionTs, name } = await ctx.runQuery(\n    internal.agent.memory.getReflectionMemories,\n    {\n      worldId,\n      playerId,\n      numberOfItems: 100,\n    },\n  );\n\n  // should only reflect if lastest 100 items have importance score of >500\n  const sumOfImportanceScore = memories\n    .filter((m) => m._creationTime > (lastReflectionTs ?? 0))\n    .reduce((acc, curr) => acc + curr.importance, 0);\n  const shouldReflect = sumOfImportanceScore > 500;\n\n  if (!shouldReflect) {\n    return false;\n  }\n  console.debug('sum of importance score = ', sumOfImportanceScore);\n  console.debug('Reflecting...');\n  const prompt = ['[no prose]', '[Output only JSON]', `You are ${name}, statements about you:`];\n  memories.forEach((m, idx) => {\n    prompt.push(`Statement ${idx}: ${m.description}`);\n  });\n  prompt.push('What 3 high-level insights can you infer from the above statements?');\n  prompt.push(\n    'Return in JSON format, where the key is a list of input statements that contributed to your insights and value is your insight. Make the response parseable by Typescript JSON.parse() function. DO NOT escape characters or include \"\\n\" or white space in response.',\n  );\n  prompt.push(\n    'Example: [{insight: \"...\", statementIds: [1,2]}, {insight: \"...\", statementIds: [1]}, ...]',\n  );\n\n  const { content: reflection } = await chatCompletion({\n    messages: [\n      {\n        role: 'user',\n        content: prompt.join('\\n'),\n      },\n    ],\n  });\n\n  try {\n    const insights = JSON.parse(reflection) as { insight: string; statementIds: number[] }[];\n    const memoriesToSave = await asyncMap(insights, async (item) => {\n      const relatedMemoryIds = item.statementIds.map((idx: number) => memories[idx]._id);\n      const importance = await calculateImportance(item.insight);\n      const { embedding } = await fetchEmbedding(item.insight);\n      console.debug('adding reflection memory...', item.insight);\n      return {\n        description: item.insight,\n        embedding,\n        importance,\n        relatedMemoryIds,\n      };\n    });\n\n    await ctx.runMutation(selfInternal.insertReflectionMemories, {\n      worldId,\n      playerId,\n      reflections: memoriesToSave,\n    });\n  } catch (e) {\n    console.error('error saving or parsing reflection', e);\n    console.debug('reflection', reflection);\n    return false;\n  }\n  return true;\n}\nexport const getReflectionMemories = internalQuery({\n  args: { worldId: v.id('worlds'), playerId, numberOfItems: v.number() },\n  handler: async (ctx, args) => {\n    const world = await ctx.db.get(args.worldId);\n    if (!world) {\n      throw new Error(`World ${args.worldId} not found`);\n    }\n    const player = world.players.find((p) => p.id === args.playerId);\n    if (!player) {\n      throw new Error(`Player ${args.playerId} not found`);\n    }\n    const playerDescription = await ctx.db\n      .query('playerDescriptions')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', args.playerId))\n      .first();\n    if (!playerDescription) {\n      throw new Error(`Player description for ${args.playerId} not found`);\n    }\n    const memories = await ctx.db\n      .query('memories')\n      .withIndex('playerId', (q) => q.eq('playerId', player.id))\n      .order('desc')\n      .take(args.numberOfItems);\n\n    const lastReflection = await ctx.db\n      .query('memories')\n      .withIndex('playerId_type', (q) =>\n        q.eq('playerId', args.playerId).eq('data.type', 'reflection'),\n      )\n      .order('desc')\n      .first();\n\n    return {\n      name: playerDescription.name,\n      memories,\n      lastReflectionTs: lastReflection?._creationTime,\n    };\n  },\n});\n\nexport async function latestMemoryOfType<T extends MemoryType>(\n  db: DatabaseReader,\n  playerId: GameId<'players'>,\n  type: T,\n) {\n  const entry = await db\n    .query('memories')\n    .withIndex('playerId_type', (q) => q.eq('playerId', playerId).eq('data.type', type))\n    .order('desc')\n    .first();\n  if (!entry) return null;\n  return entry as MemoryOfType<T>;\n}\n"
  },
  {
    "path": "convex/agent/schema.ts",
    "content": "import { v } from 'convex/values';\nimport { playerId, conversationId } from '../aiTown/ids';\nimport { defineTable } from 'convex/server';\nimport { EMBEDDING_DIMENSION } from '../util/llm';\n\nexport const memoryFields = {\n  playerId,\n  description: v.string(),\n  embeddingId: v.id('memoryEmbeddings'),\n  importance: v.number(),\n  lastAccess: v.number(),\n  data: v.union(\n    // Setting up dynamics between players\n    v.object({\n      type: v.literal('relationship'),\n      // The player this memory is about, from the perspective of the player\n      // whose memory this is.\n      playerId,\n    }),\n    v.object({\n      type: v.literal('conversation'),\n      conversationId,\n      // The other player(s) in the conversation.\n      playerIds: v.array(playerId),\n    }),\n    v.object({\n      type: v.literal('reflection'),\n      relatedMemoryIds: v.array(v.id('memories')),\n    }),\n  ),\n};\nexport const memoryTables = {\n  memories: defineTable(memoryFields)\n    .index('embeddingId', ['embeddingId'])\n    .index('playerId_type', ['playerId', 'data.type'])\n    .index('playerId', ['playerId']),\n  memoryEmbeddings: defineTable({\n    playerId,\n    embedding: v.array(v.float64()),\n  }).vectorIndex('embedding', {\n    vectorField: 'embedding',\n    filterFields: ['playerId'],\n    dimensions: EMBEDDING_DIMENSION,\n  }),\n};\n\nexport const agentTables = {\n  ...memoryTables,\n  embeddingsCache: defineTable({\n    textHash: v.bytes(),\n    embedding: v.array(v.float64()),\n  }).index('text', ['textHash']),\n};\n"
  },
  {
    "path": "convex/aiTown/agent.ts",
    "content": "import { ObjectType, v } from 'convex/values';\nimport { GameId, parseGameId } from './ids';\nimport { agentId, conversationId, playerId } from './ids';\nimport { serializedPlayer } from './player';\nimport { Game } from './game';\nimport {\n  ACTION_TIMEOUT,\n  AWKWARD_CONVERSATION_TIMEOUT,\n  CONVERSATION_COOLDOWN,\n  CONVERSATION_DISTANCE,\n  INVITE_ACCEPT_PROBABILITY,\n  INVITE_TIMEOUT,\n  MAX_CONVERSATION_DURATION,\n  MAX_CONVERSATION_MESSAGES,\n  MESSAGE_COOLDOWN,\n  MIDPOINT_THRESHOLD,\n  PLAYER_CONVERSATION_COOLDOWN,\n} from '../constants';\nimport { FunctionArgs } from 'convex/server';\nimport { MutationCtx, internalMutation, internalQuery } from '../_generated/server';\nimport { distance } from '../util/geometry';\nimport { internal } from '../_generated/api';\nimport { movePlayer } from './movement';\nimport { insertInput } from './insertInput';\n\nexport class Agent {\n  id: GameId<'agents'>;\n  playerId: GameId<'players'>;\n  toRemember?: GameId<'conversations'>;\n  lastConversation?: number;\n  lastInviteAttempt?: number;\n  inProgressOperation?: {\n    name: string;\n    operationId: string;\n    started: number;\n  };\n\n  constructor(serialized: SerializedAgent) {\n    const { id, lastConversation, lastInviteAttempt, inProgressOperation } = serialized;\n    const playerId = parseGameId('players', serialized.playerId);\n    this.id = parseGameId('agents', id);\n    this.playerId = playerId;\n    this.toRemember =\n      serialized.toRemember !== undefined\n        ? parseGameId('conversations', serialized.toRemember)\n        : undefined;\n    this.lastConversation = lastConversation;\n    this.lastInviteAttempt = lastInviteAttempt;\n    this.inProgressOperation = inProgressOperation;\n  }\n\n  tick(game: Game, now: number) {\n    const player = game.world.players.get(this.playerId);\n    if (!player) {\n      throw new Error(`Invalid player ID ${this.playerId}`);\n    }\n    if (this.inProgressOperation) {\n      if (now < this.inProgressOperation.started + ACTION_TIMEOUT) {\n        // Wait on the operation to finish.\n        return;\n      }\n      console.log(`Timing out ${JSON.stringify(this.inProgressOperation)}`);\n      delete this.inProgressOperation;\n    }\n    const conversation = game.world.playerConversation(player);\n    const member = conversation?.participants.get(player.id);\n\n    const recentlyAttemptedInvite =\n      this.lastInviteAttempt && now < this.lastInviteAttempt + CONVERSATION_COOLDOWN;\n    const doingActivity = player.activity && player.activity.until > now;\n    if (doingActivity && (conversation || player.pathfinding)) {\n      player.activity!.until = now;\n    }\n    // If we're not in a conversation, do something.\n    // If we aren't doing an activity or moving, do something.\n    // If we have been wandering but haven't thought about something to do for\n    // a while, do something.\n    if (!conversation && !doingActivity && (!player.pathfinding || !recentlyAttemptedInvite)) {\n      this.startOperation(game, now, 'agentDoSomething', {\n        worldId: game.worldId,\n        player: player.serialize(),\n        otherFreePlayers: [...game.world.players.values()]\n          .filter((p) => p.id !== player.id)\n          .filter(\n            (p) => ![...game.world.conversations.values()].find((c) => c.participants.has(p.id)),\n          )\n          .map((p) => p.serialize()),\n        agent: this.serialize(),\n        map: game.worldMap.serialize(),\n      });\n      return;\n    }\n    // Check to see if we have a conversation we need to remember.\n    if (this.toRemember) {\n      // Fire off the action to remember the conversation.\n      console.log(`Agent ${this.id} remembering conversation ${this.toRemember}`);\n      this.startOperation(game, now, 'agentRememberConversation', {\n        worldId: game.worldId,\n        playerId: this.playerId,\n        agentId: this.id,\n        conversationId: this.toRemember,\n      });\n      delete this.toRemember;\n      return;\n    }\n    if (conversation && member) {\n      const [otherPlayerId, otherMember] = [...conversation.participants.entries()].find(\n        ([id]) => id !== player.id,\n      )!;\n      const otherPlayer = game.world.players.get(otherPlayerId)!;\n      if (member.status.kind === 'invited') {\n        // Accept a conversation with another agent with some probability and with\n        // a human unconditionally.\n        if (otherPlayer.human || Math.random() < INVITE_ACCEPT_PROBABILITY) {\n          console.log(`Agent ${player.id} accepting invite from ${otherPlayer.id}`);\n          conversation.acceptInvite(game, player);\n          // Stop moving so we can start walking towards the other player.\n          if (player.pathfinding) {\n            delete player.pathfinding;\n          }\n        } else {\n          console.log(`Agent ${player.id} rejecting invite from ${otherPlayer.id}`);\n          conversation.rejectInvite(game, now, player);\n        }\n        return;\n      }\n      if (member.status.kind === 'walkingOver') {\n        // Leave a conversation if we've been waiting for too long.\n        if (member.invited + INVITE_TIMEOUT < now) {\n          console.log(`Giving up on invite to ${otherPlayer.id}`);\n          conversation.leave(game, now, player);\n          return;\n        }\n\n        // Don't keep moving around if we're near enough.\n        const playerDistance = distance(player.position, otherPlayer.position);\n        if (playerDistance < CONVERSATION_DISTANCE) {\n          return;\n        }\n\n        // Keep moving towards the other player.\n        // If we're close enough to the player, just walk to them directly.\n        if (!player.pathfinding) {\n          let destination;\n          if (playerDistance < MIDPOINT_THRESHOLD) {\n            destination = {\n              x: Math.floor(otherPlayer.position.x),\n              y: Math.floor(otherPlayer.position.y),\n            };\n          } else {\n            destination = {\n              x: Math.floor((player.position.x + otherPlayer.position.x) / 2),\n              y: Math.floor((player.position.y + otherPlayer.position.y) / 2),\n            };\n          }\n          console.log(`Agent ${player.id} walking towards ${otherPlayer.id}...`, destination);\n          movePlayer(game, now, player, destination);\n        }\n        return;\n      }\n      if (member.status.kind === 'participating') {\n        const started = member.status.started;\n        if (conversation.isTyping && conversation.isTyping.playerId !== player.id) {\n          // Wait for the other player to finish typing.\n          return;\n        }\n        if (!conversation.lastMessage) {\n          const isInitiator = conversation.creator === player.id;\n          const awkwardDeadline = started + AWKWARD_CONVERSATION_TIMEOUT;\n          // Send the first message if we're the initiator or if we've been waiting for too long.\n          if (isInitiator || awkwardDeadline < now) {\n            // Grab the lock on the conversation and send a \"start\" message.\n            console.log(`${player.id} initiating conversation with ${otherPlayer.id}.`);\n            const messageUuid = crypto.randomUUID();\n            conversation.setIsTyping(now, player, messageUuid);\n            this.startOperation(game, now, 'agentGenerateMessage', {\n              worldId: game.worldId,\n              playerId: player.id,\n              agentId: this.id,\n              conversationId: conversation.id,\n              otherPlayerId: otherPlayer.id,\n              messageUuid,\n              type: 'start',\n            });\n            return;\n          } else {\n            // Wait on the other player to say something up to the awkward deadline.\n            return;\n          }\n        }\n        // See if the conversation has been going on too long and decide to leave.\n        const tooLongDeadline = started + MAX_CONVERSATION_DURATION;\n        if (tooLongDeadline < now || conversation.numMessages > MAX_CONVERSATION_MESSAGES) {\n          console.log(`${player.id} leaving conversation with ${otherPlayer.id}.`);\n          const messageUuid = crypto.randomUUID();\n          conversation.setIsTyping(now, player, messageUuid);\n          this.startOperation(game, now, 'agentGenerateMessage', {\n            worldId: game.worldId,\n            playerId: player.id,\n            agentId: this.id,\n            conversationId: conversation.id,\n            otherPlayerId: otherPlayer.id,\n            messageUuid,\n            type: 'leave',\n          });\n          return;\n        }\n        // Wait for the awkward deadline if we sent the last message.\n        if (conversation.lastMessage.author === player.id) {\n          const awkwardDeadline = conversation.lastMessage.timestamp + AWKWARD_CONVERSATION_TIMEOUT;\n          if (now < awkwardDeadline) {\n            return;\n          }\n        }\n        // Wait for a cooldown after the last message to simulate \"reading\" the message.\n        const messageCooldown = conversation.lastMessage.timestamp + MESSAGE_COOLDOWN;\n        if (now < messageCooldown) {\n          return;\n        }\n        // Grab the lock and send a message!\n        console.log(`${player.id} continuing conversation with ${otherPlayer.id}.`);\n        const messageUuid = crypto.randomUUID();\n        conversation.setIsTyping(now, player, messageUuid);\n        this.startOperation(game, now, 'agentGenerateMessage', {\n          worldId: game.worldId,\n          playerId: player.id,\n          agentId: this.id,\n          conversationId: conversation.id,\n          otherPlayerId: otherPlayer.id,\n          messageUuid,\n          type: 'continue',\n        });\n        return;\n      }\n    }\n  }\n\n  startOperation<Name extends keyof AgentOperations>(\n    game: Game,\n    now: number,\n    name: Name,\n    args: Omit<FunctionArgs<AgentOperations[Name]>, 'operationId'>,\n  ) {\n    if (this.inProgressOperation) {\n      throw new Error(\n        `Agent ${this.id} already has an operation: ${JSON.stringify(this.inProgressOperation)}`,\n      );\n    }\n    const operationId = game.allocId('operations');\n    console.log(`Agent ${this.id} starting operation ${name} (${operationId})`);\n    game.scheduleOperation(name, { operationId, ...args } as any);\n    this.inProgressOperation = {\n      name,\n      operationId,\n      started: now,\n    };\n  }\n\n  serialize(): SerializedAgent {\n    return {\n      id: this.id,\n      playerId: this.playerId,\n      toRemember: this.toRemember,\n      lastConversation: this.lastConversation,\n      lastInviteAttempt: this.lastInviteAttempt,\n      inProgressOperation: this.inProgressOperation,\n    };\n  }\n}\n\nexport const serializedAgent = {\n  id: agentId,\n  playerId: playerId,\n  toRemember: v.optional(conversationId),\n  lastConversation: v.optional(v.number()),\n  lastInviteAttempt: v.optional(v.number()),\n  inProgressOperation: v.optional(\n    v.object({\n      name: v.string(),\n      operationId: v.string(),\n      started: v.number(),\n    }),\n  ),\n};\nexport type SerializedAgent = ObjectType<typeof serializedAgent>;\n\ntype AgentOperations = typeof internal.aiTown.agentOperations;\n\nexport async function runAgentOperation(ctx: MutationCtx, operation: string, args: any) {\n  let reference;\n  switch (operation) {\n    case 'agentRememberConversation':\n      reference = internal.aiTown.agentOperations.agentRememberConversation;\n      break;\n    case 'agentGenerateMessage':\n      reference = internal.aiTown.agentOperations.agentGenerateMessage;\n      break;\n    case 'agentDoSomething':\n      reference = internal.aiTown.agentOperations.agentDoSomething;\n      break;\n    default:\n      throw new Error(`Unknown operation: ${operation}`);\n  }\n  await ctx.scheduler.runAfter(0, reference, args);\n}\n\nexport const agentSendMessage = internalMutation({\n  args: {\n    worldId: v.id('worlds'),\n    conversationId,\n    agentId,\n    playerId,\n    text: v.string(),\n    messageUuid: v.string(),\n    leaveConversation: v.boolean(),\n    operationId: v.string(),\n  },\n  handler: async (ctx, args) => {\n    await ctx.db.insert('messages', {\n      conversationId: args.conversationId,\n      author: args.playerId,\n      text: args.text,\n      messageUuid: args.messageUuid,\n      worldId: args.worldId,\n    });\n    await insertInput(ctx, args.worldId, 'agentFinishSendingMessage', {\n      conversationId: args.conversationId,\n      agentId: args.agentId,\n      timestamp: Date.now(),\n      leaveConversation: args.leaveConversation,\n      operationId: args.operationId,\n    });\n  },\n});\n\nexport const findConversationCandidate = internalQuery({\n  args: {\n    now: v.number(),\n    worldId: v.id('worlds'),\n    player: v.object(serializedPlayer),\n    otherFreePlayers: v.array(v.object(serializedPlayer)),\n  },\n  handler: async (ctx, { now, worldId, player, otherFreePlayers }) => {\n    const { position } = player;\n    const candidates = [];\n\n    for (const otherPlayer of otherFreePlayers) {\n      // Find the latest conversation we're both members of.\n      const lastMember = await ctx.db\n        .query('participatedTogether')\n        .withIndex('edge', (q) =>\n          q.eq('worldId', worldId).eq('player1', player.id).eq('player2', otherPlayer.id),\n        )\n        .order('desc')\n        .first();\n      if (lastMember) {\n        if (now < lastMember.ended + PLAYER_CONVERSATION_COOLDOWN) {\n          continue;\n        }\n      }\n      candidates.push({ id: otherPlayer.id, position });\n    }\n\n    // Sort by distance and take the nearest candidate.\n    candidates.sort((a, b) => distance(a.position, position) - distance(b.position, position));\n    return candidates[0]?.id;\n  },\n});\n"
  },
  {
    "path": "convex/aiTown/agentDescription.ts",
    "content": "import { ObjectType, v } from 'convex/values';\nimport { GameId, agentId, parseGameId } from './ids';\n\nexport class AgentDescription {\n  agentId: GameId<'agents'>;\n  identity: string;\n  plan: string;\n\n  constructor(serialized: SerializedAgentDescription) {\n    const { agentId, identity, plan } = serialized;\n    this.agentId = parseGameId('agents', agentId);\n    this.identity = identity;\n    this.plan = plan;\n  }\n\n  serialize(): SerializedAgentDescription {\n    const { agentId, identity, plan } = this;\n    return { agentId, identity, plan };\n  }\n}\n\nexport const serializedAgentDescription = {\n  agentId,\n  identity: v.string(),\n  plan: v.string(),\n};\nexport type SerializedAgentDescription = ObjectType<typeof serializedAgentDescription>;\n"
  },
  {
    "path": "convex/aiTown/agentInputs.ts",
    "content": "import { v } from 'convex/values';\nimport { agentId, conversationId, parseGameId } from './ids';\nimport { Player, activity } from './player';\nimport { Conversation, conversationInputs } from './conversation';\nimport { movePlayer } from './movement';\nimport { inputHandler } from './inputHandler';\nimport { point } from '../util/types';\nimport { Descriptions } from '../../data/characters';\nimport { AgentDescription } from './agentDescription';\nimport { Agent } from './agent';\n\nexport const agentInputs = {\n  finishRememberConversation: inputHandler({\n    args: {\n      operationId: v.string(),\n      agentId,\n    },\n    handler: (game, now, args) => {\n      const agentId = parseGameId('agents', args.agentId);\n      const agent = game.world.agents.get(agentId);\n      if (!agent) {\n        throw new Error(`Couldn't find agent: ${agentId}`);\n      }\n      if (\n        !agent.inProgressOperation ||\n        agent.inProgressOperation.operationId !== args.operationId\n      ) {\n        console.debug(`Agent ${agentId} isn't remembering ${args.operationId}`);\n      } else {\n        delete agent.inProgressOperation;\n        delete agent.toRemember;\n      }\n      return null;\n    },\n  }),\n  finishDoSomething: inputHandler({\n    args: {\n      operationId: v.string(),\n      agentId: v.id('agents'),\n      destination: v.optional(point),\n      invitee: v.optional(v.id('players')),\n      activity: v.optional(activity),\n    },\n    handler: (game, now, args) => {\n      const agentId = parseGameId('agents', args.agentId);\n      const agent = game.world.agents.get(agentId);\n      if (!agent) {\n        throw new Error(`Couldn't find agent: ${agentId}`);\n      }\n      if (\n        !agent.inProgressOperation ||\n        agent.inProgressOperation.operationId !== args.operationId\n      ) {\n        console.debug(`Agent ${agentId} didn't have ${args.operationId} in progress`);\n        return null;\n      }\n      delete agent.inProgressOperation;\n      const player = game.world.players.get(agent.playerId)!;\n      if (args.invitee) {\n        const inviteeId = parseGameId('players', args.invitee);\n        const invitee = game.world.players.get(inviteeId);\n        if (!invitee) {\n          throw new Error(`Couldn't find player: ${inviteeId}`);\n        }\n        Conversation.start(game, now, player, invitee);\n        agent.lastInviteAttempt = now;\n      }\n      if (args.destination) {\n        movePlayer(game, now, player, args.destination);\n      }\n      if (args.activity) {\n        player.activity = args.activity;\n      }\n      return null;\n    },\n  }),\n  agentFinishSendingMessage: inputHandler({\n    args: {\n      agentId,\n      conversationId,\n      timestamp: v.number(),\n      operationId: v.string(),\n      leaveConversation: v.boolean(),\n    },\n    handler: (game, now, args) => {\n      const agentId = parseGameId('agents', args.agentId);\n      const agent = game.world.agents.get(agentId);\n      if (!agent) {\n        throw new Error(`Couldn't find agent: ${agentId}`);\n      }\n      const player = game.world.players.get(agent.playerId);\n      if (!player) {\n        throw new Error(`Couldn't find player: ${agent.playerId}`);\n      }\n      const conversationId = parseGameId('conversations', args.conversationId);\n      const conversation = game.world.conversations.get(conversationId);\n      if (!conversation) {\n        throw new Error(`Couldn't find conversation: ${conversationId}`);\n      }\n      if (\n        !agent.inProgressOperation ||\n        agent.inProgressOperation.operationId !== args.operationId\n      ) {\n        console.debug(`Agent ${agentId} wasn't sending a message ${args.operationId}`);\n        return null;\n      }\n      delete agent.inProgressOperation;\n      conversationInputs.finishSendingMessage.handler(game, now, {\n        playerId: agent.playerId,\n        conversationId: args.conversationId,\n        timestamp: args.timestamp,\n      });\n      if (args.leaveConversation) {\n        conversation.leave(game, now, player);\n      }\n      return null;\n    },\n  }),\n  createAgent: inputHandler({\n    args: {\n      descriptionIndex: v.number(),\n    },\n    handler: (game, now, args) => {\n      const description = Descriptions[args.descriptionIndex];\n      const playerId = Player.join(\n        game,\n        now,\n        description.name,\n        description.character,\n        description.identity,\n      );\n      const agentId = game.allocId('agents');\n      game.world.agents.set(\n        agentId,\n        new Agent({\n          id: agentId,\n          playerId: playerId,\n          inProgressOperation: undefined,\n          lastConversation: undefined,\n          lastInviteAttempt: undefined,\n          toRemember: undefined,\n        }),\n      );\n      game.agentDescriptions.set(\n        agentId,\n        new AgentDescription({\n          agentId: agentId,\n          identity: description.identity,\n          plan: description.plan,\n        }),\n      );\n      return { agentId };\n    },\n  }),\n};\n"
  },
  {
    "path": "convex/aiTown/agentOperations.ts",
    "content": "import { v } from 'convex/values';\nimport { internalAction } from '../_generated/server';\nimport { WorldMap, serializedWorldMap } from './worldMap';\nimport { rememberConversation } from '../agent/memory';\nimport { GameId, agentId, conversationId, playerId } from './ids';\nimport {\n  continueConversationMessage,\n  leaveConversationMessage,\n  startConversationMessage,\n} from '../agent/conversation';\nimport { assertNever } from '../util/assertNever';\nimport { serializedAgent } from './agent';\nimport { ACTIVITIES, ACTIVITY_COOLDOWN, CONVERSATION_COOLDOWN } from '../constants';\nimport { api, internal } from '../_generated/api';\nimport { sleep } from '../util/sleep';\nimport { serializedPlayer } from './player';\n\nexport const agentRememberConversation = internalAction({\n  args: {\n    worldId: v.id('worlds'),\n    playerId,\n    agentId,\n    conversationId,\n    operationId: v.string(),\n  },\n  handler: async (ctx, args) => {\n    await rememberConversation(\n      ctx,\n      args.worldId,\n      args.agentId as GameId<'agents'>,\n      args.playerId as GameId<'players'>,\n      args.conversationId as GameId<'conversations'>,\n    );\n    await sleep(Math.random() * 1000);\n    await ctx.runMutation(api.aiTown.main.sendInput, {\n      worldId: args.worldId,\n      name: 'finishRememberConversation',\n      args: {\n        agentId: args.agentId,\n        operationId: args.operationId,\n      },\n    });\n  },\n});\n\nexport const agentGenerateMessage = internalAction({\n  args: {\n    worldId: v.id('worlds'),\n    playerId,\n    agentId,\n    conversationId,\n    otherPlayerId: playerId,\n    operationId: v.string(),\n    type: v.union(v.literal('start'), v.literal('continue'), v.literal('leave')),\n    messageUuid: v.string(),\n  },\n  handler: async (ctx, args) => {\n    let completionFn;\n    switch (args.type) {\n      case 'start':\n        completionFn = startConversationMessage;\n        break;\n      case 'continue':\n        completionFn = continueConversationMessage;\n        break;\n      case 'leave':\n        completionFn = leaveConversationMessage;\n        break;\n      default:\n        assertNever(args.type);\n    }\n    const text = await completionFn(\n      ctx,\n      args.worldId,\n      args.conversationId as GameId<'conversations'>,\n      args.playerId as GameId<'players'>,\n      args.otherPlayerId as GameId<'players'>,\n    );\n\n    await ctx.runMutation(internal.aiTown.agent.agentSendMessage, {\n      worldId: args.worldId,\n      conversationId: args.conversationId,\n      agentId: args.agentId,\n      playerId: args.playerId,\n      text,\n      messageUuid: args.messageUuid,\n      leaveConversation: args.type === 'leave',\n      operationId: args.operationId,\n    });\n  },\n});\n\nexport const agentDoSomething = internalAction({\n  args: {\n    worldId: v.id('worlds'),\n    player: v.object(serializedPlayer),\n    agent: v.object(serializedAgent),\n    map: v.object(serializedWorldMap),\n    otherFreePlayers: v.array(v.object(serializedPlayer)),\n    operationId: v.string(),\n  },\n  handler: async (ctx, args) => {\n    const { player, agent } = args;\n    const map = new WorldMap(args.map);\n    const now = Date.now();\n    // Don't try to start a new conversation if we were just in one.\n    const justLeftConversation =\n      agent.lastConversation && now < agent.lastConversation + CONVERSATION_COOLDOWN;\n    // Don't try again if we recently tried to find someone to invite.\n    const recentlyAttemptedInvite =\n      agent.lastInviteAttempt && now < agent.lastInviteAttempt + CONVERSATION_COOLDOWN;\n    const recentActivity = player.activity && now < player.activity.until + ACTIVITY_COOLDOWN;\n    // Decide whether to do an activity or wander somewhere.\n    if (!player.pathfinding) {\n      if (recentActivity || justLeftConversation) {\n        await sleep(Math.random() * 1000);\n        await ctx.runMutation(api.aiTown.main.sendInput, {\n          worldId: args.worldId,\n          name: 'finishDoSomething',\n          args: {\n            operationId: args.operationId,\n            agentId: agent.id,\n            destination: wanderDestination(map),\n          },\n        });\n        return;\n      } else {\n        // TODO: have LLM choose the activity & emoji\n        const activity = ACTIVITIES[Math.floor(Math.random() * ACTIVITIES.length)];\n        await sleep(Math.random() * 1000);\n        await ctx.runMutation(api.aiTown.main.sendInput, {\n          worldId: args.worldId,\n          name: 'finishDoSomething',\n          args: {\n            operationId: args.operationId,\n            agentId: agent.id,\n            activity: {\n              description: activity.description,\n              emoji: activity.emoji,\n              until: Date.now() + activity.duration,\n            },\n          },\n        });\n        return;\n      }\n    }\n    const invitee =\n      justLeftConversation || recentlyAttemptedInvite\n        ? undefined\n        : await ctx.runQuery(internal.aiTown.agent.findConversationCandidate, {\n            now,\n            worldId: args.worldId,\n            player: args.player,\n            otherFreePlayers: args.otherFreePlayers,\n          });\n\n    // TODO: We hit a lot of OCC errors on sending inputs in this file. It's\n    // easy for them to get scheduled at the same time and line up in time.\n    await sleep(Math.random() * 1000);\n    await ctx.runMutation(api.aiTown.main.sendInput, {\n      worldId: args.worldId,\n      name: 'finishDoSomething',\n      args: {\n        operationId: args.operationId,\n        agentId: args.agent.id,\n        invitee,\n      },\n    });\n  },\n});\n\nfunction wanderDestination(worldMap: WorldMap) {\n  // Wander someonewhere at least one tile away from the edge.\n  return {\n    x: 1 + Math.floor(Math.random() * (worldMap.width - 2)),\n    y: 1 + Math.floor(Math.random() * (worldMap.height - 2)),\n  };\n}\n"
  },
  {
    "path": "convex/aiTown/conversation.ts",
    "content": "import { ObjectType, v } from 'convex/values';\nimport { GameId, parseGameId } from './ids';\nimport { conversationId, playerId } from './ids';\nimport { Player } from './player';\nimport { inputHandler } from './inputHandler';\n\nimport { TYPING_TIMEOUT, CONVERSATION_DISTANCE } from '../constants';\nimport { distance, normalize, vector } from '../util/geometry';\nimport { Point } from '../util/types';\nimport { Game } from './game';\nimport { stopPlayer, blocked, movePlayer } from './movement';\nimport { ConversationMembership, serializedConversationMembership } from './conversationMembership';\nimport { parseMap, serializeMap } from '../util/object';\n\nexport class Conversation {\n  id: GameId<'conversations'>;\n  creator: GameId<'players'>;\n  created: number;\n  isTyping?: {\n    playerId: GameId<'players'>;\n    messageUuid: string;\n    since: number;\n  };\n  lastMessage?: {\n    author: GameId<'players'>;\n    timestamp: number;\n  };\n  numMessages: number;\n  participants: Map<GameId<'players'>, ConversationMembership>;\n\n  constructor(serialized: SerializedConversation) {\n    const { id, creator, created, isTyping, lastMessage, numMessages, participants } = serialized;\n    this.id = parseGameId('conversations', id);\n    this.creator = parseGameId('players', creator);\n    this.created = created;\n    this.isTyping = isTyping && {\n      playerId: parseGameId('players', isTyping.playerId),\n      messageUuid: isTyping.messageUuid,\n      since: isTyping.since,\n    };\n    this.lastMessage = lastMessage && {\n      author: parseGameId('players', lastMessage.author),\n      timestamp: lastMessage.timestamp,\n    };\n    this.numMessages = numMessages;\n    this.participants = parseMap(participants, ConversationMembership, (m) => m.playerId);\n  }\n\n  tick(game: Game, now: number) {\n    if (this.isTyping && this.isTyping.since + TYPING_TIMEOUT < now) {\n      delete this.isTyping;\n    }\n    if (this.participants.size !== 2) {\n      console.warn(`Conversation ${this.id} has ${this.participants.size} participants`);\n      return;\n    }\n    const [playerId1, playerId2] = [...this.participants.keys()];\n    const member1 = this.participants.get(playerId1)!;\n    const member2 = this.participants.get(playerId2)!;\n\n    const player1 = game.world.players.get(playerId1)!;\n    const player2 = game.world.players.get(playerId2)!;\n\n    const playerDistance = distance(player1?.position, player2?.position);\n\n    // If the players are both in the \"walkingOver\" state and they're sufficiently close, transition both\n    // of them to \"participating\" and stop their paths.\n    if (member1.status.kind === 'walkingOver' && member2.status.kind === 'walkingOver') {\n      if (playerDistance < CONVERSATION_DISTANCE) {\n        console.log(`Starting conversation between ${player1.id} and ${player2.id}`);\n\n        // First, stop the two players from moving.\n        stopPlayer(player1);\n        stopPlayer(player2);\n\n        member1.status = { kind: 'participating', started: now };\n        member2.status = { kind: 'participating', started: now };\n\n        // Try to move the first player to grid point nearest the other player.\n        const neighbors = (p: Point) => [\n          { x: p.x + 1, y: p.y },\n          { x: p.x - 1, y: p.y },\n          { x: p.x, y: p.y + 1 },\n          { x: p.x, y: p.y - 1 },\n        ];\n        const floorPos1 = { x: Math.floor(player1.position.x), y: Math.floor(player1.position.y) };\n        const p1Candidates = neighbors(floorPos1).filter((p) => !blocked(game, now, p, player1.id));\n        p1Candidates.sort((a, b) => distance(a, player2.position) - distance(b, player2.position));\n        if (p1Candidates.length > 0) {\n          const p1Candidate = p1Candidates[0];\n\n          // Try to move the second player to the grid point nearest the first player's\n          // destination.\n          const p2Candidates = neighbors(p1Candidate).filter(\n            (p) => !blocked(game, now, p, player2.id),\n          );\n          p2Candidates.sort(\n            (a, b) => distance(a, player2.position) - distance(b, player2.position),\n          );\n          if (p2Candidates.length > 0) {\n            const p2Candidate = p2Candidates[0];\n            movePlayer(game, now, player1, p1Candidate, true);\n            movePlayer(game, now, player2, p2Candidate, true);\n          }\n        }\n      }\n    }\n\n    // Orient the two players towards each other if they're not moving.\n    if (member1.status.kind === 'participating' && member2.status.kind === 'participating') {\n      const v = normalize(vector(player1.position, player2.position));\n      if (!player1.pathfinding && v) {\n        player1.facing = v;\n      }\n      if (!player2.pathfinding && v) {\n        player2.facing.dx = -v.dx;\n        player2.facing.dy = -v.dy;\n      }\n    }\n  }\n\n  static start(game: Game, now: number, player: Player, invitee: Player) {\n    if (player.id === invitee.id) {\n      throw new Error(`Can't invite yourself to a conversation`);\n    }\n    // Ensure the players still exist.\n    if ([...game.world.conversations.values()].find((c) => c.participants.has(player.id))) {\n      const reason = `Player ${player.id} is already in a conversation`;\n      console.log(reason);\n      return { error: reason };\n    }\n    if ([...game.world.conversations.values()].find((c) => c.participants.has(invitee.id))) {\n      const reason = `Player ${player.id} is already in a conversation`;\n      console.log(reason);\n      return { error: reason };\n    }\n    const conversationId = game.allocId('conversations');\n    console.log(`Creating conversation ${conversationId}`);\n    game.world.conversations.set(\n      conversationId,\n      new Conversation({\n        id: conversationId,\n        created: now,\n        creator: player.id,\n        numMessages: 0,\n        participants: [\n          { playerId: player.id, invited: now, status: { kind: 'walkingOver' } },\n          { playerId: invitee.id, invited: now, status: { kind: 'invited' } },\n        ],\n      }),\n    );\n    return { conversationId };\n  }\n\n  setIsTyping(now: number, player: Player, messageUuid: string) {\n    if (this.isTyping) {\n      if (this.isTyping.playerId !== player.id) {\n        throw new Error(`Player ${this.isTyping.playerId} is already typing in ${this.id}`);\n      }\n      return;\n    }\n    this.isTyping = { playerId: player.id, messageUuid, since: now };\n  }\n\n  acceptInvite(game: Game, player: Player) {\n    const member = this.participants.get(player.id);\n    if (!member) {\n      throw new Error(`Player ${player.id} not in conversation ${this.id}`);\n    }\n    if (member.status.kind !== 'invited') {\n      throw new Error(\n        `Invalid membership status for ${player.id}:${this.id}: ${JSON.stringify(member)}`,\n      );\n    }\n    member.status = { kind: 'walkingOver' };\n  }\n\n  rejectInvite(game: Game, now: number, player: Player) {\n    const member = this.participants.get(player.id);\n    if (!member) {\n      throw new Error(`Player ${player.id} not in conversation ${this.id}`);\n    }\n    if (member.status.kind !== 'invited') {\n      throw new Error(\n        `Rejecting invite in wrong membership state: ${this.id}:${player.id}: ${JSON.stringify(\n          member,\n        )}`,\n      );\n    }\n    this.stop(game, now);\n  }\n\n  stop(game: Game, now: number) {\n    delete this.isTyping;\n    for (const [playerId, member] of this.participants.entries()) {\n      const agent = [...game.world.agents.values()].find((a) => a.playerId === playerId);\n      if (agent) {\n        agent.lastConversation = now;\n        agent.toRemember = this.id;\n      }\n    }\n    game.world.conversations.delete(this.id);\n  }\n\n  leave(game: Game, now: number, player: Player) {\n    const member = this.participants.get(player.id);\n    if (!member) {\n      throw new Error(`Couldn't find membership for ${this.id}:${player.id}`);\n    }\n    this.stop(game, now);\n  }\n\n  serialize(): SerializedConversation {\n    const { id, creator, created, isTyping, lastMessage, numMessages } = this;\n    return {\n      id,\n      creator,\n      created,\n      isTyping,\n      lastMessage,\n      numMessages,\n      participants: serializeMap(this.participants),\n    };\n  }\n}\n\nexport const serializedConversation = {\n  id: conversationId,\n  creator: playerId,\n  created: v.number(),\n  isTyping: v.optional(\n    v.object({\n      playerId,\n      messageUuid: v.string(),\n      since: v.number(),\n    }),\n  ),\n  lastMessage: v.optional(\n    v.object({\n      author: playerId,\n      timestamp: v.number(),\n    }),\n  ),\n  numMessages: v.number(),\n  participants: v.array(v.object(serializedConversationMembership)),\n};\nexport type SerializedConversation = ObjectType<typeof serializedConversation>;\n\nexport const conversationInputs = {\n  // Start a conversation, inviting the specified player.\n  // Conversations can only have two participants for now,\n  // so we don't have a separate \"invite\" input.\n  startConversation: inputHandler({\n    args: {\n      playerId,\n      invitee: playerId,\n    },\n    handler: (game: Game, now: number, args): GameId<'conversations'> => {\n      const playerId = parseGameId('players', args.playerId);\n      const player = game.world.players.get(playerId);\n      if (!player) {\n        throw new Error(`Invalid player ID: ${playerId}`);\n      }\n      const inviteeId = parseGameId('players', args.invitee);\n      const invitee = game.world.players.get(inviteeId);\n      if (!invitee) {\n        throw new Error(`Invalid player ID: ${inviteeId}`);\n      }\n      console.log(`Starting ${playerId} ${inviteeId}...`);\n      const { conversationId, error } = Conversation.start(game, now, player, invitee);\n      if (!conversationId) {\n        // TODO: pass it back to the client for them to show an error.\n        throw new Error(error);\n      }\n      return conversationId;\n    },\n  }),\n\n  startTyping: inputHandler({\n    args: {\n      playerId,\n      conversationId,\n      messageUuid: v.string(),\n    },\n    handler: (game: Game, now: number, args): null => {\n      const playerId = parseGameId('players', args.playerId);\n      const player = game.world.players.get(playerId);\n      if (!player) {\n        throw new Error(`Invalid player ID: ${playerId}`);\n      }\n      const conversationId = parseGameId('conversations', args.conversationId);\n      const conversation = game.world.conversations.get(conversationId);\n      if (!conversation) {\n        throw new Error(`Invalid conversation ID: ${conversationId}`);\n      }\n      if (conversation.isTyping && conversation.isTyping.playerId !== playerId) {\n        throw new Error(\n          `Player ${conversation.isTyping.playerId} is already typing in ${conversationId}`,\n        );\n      }\n      conversation.isTyping = { playerId, messageUuid: args.messageUuid, since: now };\n      return null;\n    },\n  }),\n\n  finishSendingMessage: inputHandler({\n    args: {\n      playerId,\n      conversationId,\n      timestamp: v.number(),\n    },\n    handler: (game: Game, now: number, args): null => {\n      const playerId = parseGameId('players', args.playerId);\n      const conversationId = parseGameId('conversations', args.conversationId);\n      const conversation = game.world.conversations.get(conversationId);\n      if (!conversation) {\n        throw new Error(`Invalid conversation ID: ${conversationId}`);\n      }\n      if (conversation.isTyping && conversation.isTyping.playerId === playerId) {\n        delete conversation.isTyping;\n      }\n      conversation.lastMessage = { author: playerId, timestamp: args.timestamp };\n      conversation.numMessages++;\n      return null;\n    },\n  }),\n\n  // Accept an invite to a conversation, which puts the\n  // player in the \"walkingOver\" state until they're close\n  // enough to the other participant.\n  acceptInvite: inputHandler({\n    args: {\n      playerId,\n      conversationId,\n    },\n    handler: (game: Game, now: number, args): null => {\n      const playerId = parseGameId('players', args.playerId);\n      const player = game.world.players.get(playerId);\n      if (!player) {\n        throw new Error(`Invalid player ID ${playerId}`);\n      }\n      const conversationId = parseGameId('conversations', args.conversationId);\n      const conversation = game.world.conversations.get(conversationId);\n      if (!conversation) {\n        throw new Error(`Invalid conversation ID ${conversationId}`);\n      }\n      conversation.acceptInvite(game, player);\n      return null;\n    },\n  }),\n\n  // Reject the invite. Eventually we might add a message\n  // that explains why!\n  rejectInvite: inputHandler({\n    args: {\n      playerId,\n      conversationId,\n    },\n    handler: (game: Game, now: number, args): null => {\n      const playerId = parseGameId('players', args.playerId);\n      const player = game.world.players.get(playerId);\n      if (!player) {\n        throw new Error(`Invalid player ID ${playerId}`);\n      }\n      const conversationId = parseGameId('conversations', args.conversationId);\n      const conversation = game.world.conversations.get(conversationId);\n      if (!conversation) {\n        throw new Error(`Invalid conversation ID ${conversationId}`);\n      }\n      conversation.rejectInvite(game, now, player);\n      return null;\n    },\n  }),\n  // Leave a conversation.\n  leaveConversation: inputHandler({\n    args: {\n      playerId,\n      conversationId,\n    },\n    handler: (game: Game, now: number, args): null => {\n      const playerId = parseGameId('players', args.playerId);\n      const player = game.world.players.get(playerId);\n      if (!player) {\n        throw new Error(`Invalid player ID ${playerId}`);\n      }\n      const conversationId = parseGameId('conversations', args.conversationId);\n      const conversation = game.world.conversations.get(conversationId);\n      if (!conversation) {\n        throw new Error(`Invalid conversation ID ${conversationId}`);\n      }\n      conversation.leave(game, now, player);\n      return null;\n    },\n  }),\n};\n"
  },
  {
    "path": "convex/aiTown/conversationMembership.ts",
    "content": "import { ObjectType, v } from 'convex/values';\nimport { GameId, parseGameId, playerId } from './ids';\n\nexport const serializedConversationMembership = {\n  playerId,\n  invited: v.number(),\n  status: v.union(\n    v.object({ kind: v.literal('invited') }),\n    v.object({ kind: v.literal('walkingOver') }),\n    v.object({ kind: v.literal('participating'), started: v.number() }),\n  ),\n};\nexport type SerializedConversationMembership = ObjectType<typeof serializedConversationMembership>;\n\nexport class ConversationMembership {\n  playerId: GameId<'players'>;\n  invited: number;\n  status:\n    | { kind: 'invited' }\n    | { kind: 'walkingOver' }\n    | { kind: 'participating'; started: number };\n\n  constructor(serialized: SerializedConversationMembership) {\n    const { playerId, invited, status } = serialized;\n    this.playerId = parseGameId('players', playerId);\n    this.invited = invited;\n    this.status = status;\n  }\n\n  serialize(): SerializedConversationMembership {\n    const { playerId, invited, status } = this;\n    return {\n      playerId,\n      invited,\n      status,\n    };\n  }\n}\n"
  },
  {
    "path": "convex/aiTown/game.ts",
    "content": "import { Infer, v } from 'convex/values';\nimport { Doc, Id } from '../_generated/dataModel';\nimport {\n  ActionCtx,\n  DatabaseReader,\n  MutationCtx,\n  internalMutation,\n  internalQuery,\n} from '../_generated/server';\nimport { World, serializedWorld } from './world';\nimport { WorldMap, serializedWorldMap } from './worldMap';\nimport { PlayerDescription, serializedPlayerDescription } from './playerDescription';\nimport { Location, locationFields, playerLocation } from './location';\nimport { runAgentOperation } from './agent';\nimport { GameId, IdTypes, allocGameId } from './ids';\nimport { InputArgs, InputNames, inputs } from './inputs';\nimport {\n  AbstractGame,\n  EngineUpdate,\n  applyEngineUpdate,\n  engineUpdate,\n  loadEngine,\n} from '../engine/abstractGame';\nimport { internal } from '../_generated/api';\nimport { HistoricalObject } from '../engine/historicalObject';\nimport { AgentDescription, serializedAgentDescription } from './agentDescription';\nimport { parseMap, serializeMap } from '../util/object';\n\nconst gameState = v.object({\n  world: v.object(serializedWorld),\n  playerDescriptions: v.array(v.object(serializedPlayerDescription)),\n  agentDescriptions: v.array(v.object(serializedAgentDescription)),\n  worldMap: v.object(serializedWorldMap),\n});\ntype GameState = Infer<typeof gameState>;\n\nconst gameStateDiff = v.object({\n  world: v.object(serializedWorld),\n  playerDescriptions: v.optional(v.array(v.object(serializedPlayerDescription))),\n  agentDescriptions: v.optional(v.array(v.object(serializedAgentDescription))),\n  worldMap: v.optional(v.object(serializedWorldMap)),\n  agentOperations: v.array(v.object({ name: v.string(), args: v.any() })),\n});\ntype GameStateDiff = Infer<typeof gameStateDiff>;\n\nexport class Game extends AbstractGame {\n  tickDuration = 16;\n  stepDuration = 1000;\n  maxTicksPerStep = 600;\n  maxInputsPerStep = 32;\n\n  world: World;\n\n  historicalLocations: Map<GameId<'players'>, HistoricalObject<Location>>;\n\n  descriptionsModified: boolean;\n  worldMap: WorldMap;\n  playerDescriptions: Map<GameId<'players'>, PlayerDescription>;\n  agentDescriptions: Map<GameId<'agents'>, AgentDescription>;\n\n  pendingOperations: Array<{ name: string; args: any }> = [];\n\n  numPathfinds: number;\n\n  constructor(\n    engine: Doc<'engines'>,\n    public worldId: Id<'worlds'>,\n    state: GameState,\n  ) {\n    super(engine);\n\n    this.world = new World(state.world);\n    delete this.world.historicalLocations;\n\n    this.descriptionsModified = false;\n    this.worldMap = new WorldMap(state.worldMap);\n    this.agentDescriptions = parseMap(state.agentDescriptions, AgentDescription, (a) => a.agentId);\n    this.playerDescriptions = parseMap(\n      state.playerDescriptions,\n      PlayerDescription,\n      (p) => p.playerId,\n    );\n\n    this.historicalLocations = new Map();\n\n    this.numPathfinds = 0;\n  }\n\n  static async load(\n    db: DatabaseReader,\n    worldId: Id<'worlds'>,\n    generationNumber: number,\n  ): Promise<{ engine: Doc<'engines'>; gameState: GameState }> {\n    const worldDoc = await db.get(worldId);\n    if (!worldDoc) {\n      throw new Error(`No world found with id ${worldId}`);\n    }\n    const worldStatus = await db\n      .query('worldStatus')\n      .withIndex('worldId', (q) => q.eq('worldId', worldId))\n      .unique();\n    if (!worldStatus) {\n      throw new Error(`No engine found for world ${worldId}`);\n    }\n    const engine = await loadEngine(db, worldStatus.engineId, generationNumber);\n    const playerDescriptionsDocs = await db\n      .query('playerDescriptions')\n      .withIndex('worldId', (q) => q.eq('worldId', worldId))\n      .collect();\n    const agentDescriptionsDocs = await db\n      .query('agentDescriptions')\n      .withIndex('worldId', (q) => q.eq('worldId', worldId))\n      .collect();\n    const worldMapDoc = await db\n      .query('maps')\n      .withIndex('worldId', (q) => q.eq('worldId', worldId))\n      .unique();\n    if (!worldMapDoc) {\n      throw new Error(`No map found for world ${worldId}`);\n    }\n    // Discard the system fields and historicalLocations from the world state.\n    const { _id, _creationTime, historicalLocations: _, ...world } = worldDoc;\n    const playerDescriptions = playerDescriptionsDocs\n      // Discard player descriptions for players that no longer exist.\n      .filter((d) => !!world.players.find((p) => p.id === d.playerId))\n      .map(({ _id, _creationTime, worldId: _, ...doc }) => doc);\n    const agentDescriptions = agentDescriptionsDocs\n      .filter((a) => !!world.agents.find((p) => p.id === a.agentId))\n      .map(({ _id, _creationTime, worldId: _, ...doc }) => doc);\n    const {\n      _id: _mapId,\n      _creationTime: _mapCreationTime,\n      worldId: _mapWorldId,\n      ...worldMap\n    } = worldMapDoc;\n    return {\n      engine,\n      gameState: {\n        world,\n        playerDescriptions,\n        agentDescriptions,\n        worldMap,\n      },\n    };\n  }\n\n  allocId<T extends IdTypes>(idType: T): GameId<T> {\n    const id = allocGameId(idType, this.world.nextId);\n    this.world.nextId += 1;\n    return id;\n  }\n\n  scheduleOperation(name: string, args: unknown) {\n    this.pendingOperations.push({ name, args });\n  }\n\n  handleInput<Name extends InputNames>(now: number, name: Name, args: InputArgs<Name>) {\n    const handler = inputs[name]?.handler;\n    if (!handler) {\n      throw new Error(`Invalid input: ${name}`);\n    }\n    return handler(this, now, args as any);\n  }\n\n  beginStep(_now: number) {\n    // Store the current location of all players in the history tracking buffer.\n    this.historicalLocations.clear();\n    for (const player of this.world.players.values()) {\n      this.historicalLocations.set(\n        player.id,\n        new HistoricalObject(locationFields, playerLocation(player)),\n      );\n    }\n    this.numPathfinds = 0;\n  }\n\n  tick(now: number) {\n    for (const player of this.world.players.values()) {\n      player.tick(this, now);\n    }\n    for (const player of this.world.players.values()) {\n      player.tickPathfinding(this, now);\n    }\n    for (const player of this.world.players.values()) {\n      player.tickPosition(this, now);\n    }\n    for (const conversation of this.world.conversations.values()) {\n      conversation.tick(this, now);\n    }\n    for (const agent of this.world.agents.values()) {\n      agent.tick(this, now);\n    }\n\n    // Save each player's location into the history buffer at the end of\n    // each tick.\n    for (const player of this.world.players.values()) {\n      let historicalObject = this.historicalLocations.get(player.id);\n      if (!historicalObject) {\n        historicalObject = new HistoricalObject(locationFields, playerLocation(player));\n        this.historicalLocations.set(player.id, historicalObject);\n      }\n      historicalObject.update(now, playerLocation(player));\n    }\n  }\n\n  async saveStep(ctx: ActionCtx, engineUpdate: EngineUpdate): Promise<void> {\n    const diff = this.takeDiff();\n    await ctx.runMutation(internal.aiTown.game.saveWorld, {\n      engineId: this.engine._id,\n      engineUpdate,\n      worldId: this.worldId,\n      worldDiff: diff,\n    });\n  }\n\n  takeDiff(): GameStateDiff {\n    const historicalLocations = [];\n    let bufferSize = 0;\n    for (const [id, historicalObject] of this.historicalLocations.entries()) {\n      const buffer = historicalObject.pack();\n      if (!buffer) {\n        continue;\n      }\n      historicalLocations.push({ playerId: id, location: buffer });\n      bufferSize += buffer.byteLength;\n    }\n    if (bufferSize > 0) {\n      console.debug(\n        `Packed ${Object.entries(historicalLocations).length} history buffers in ${(\n          bufferSize / 1024\n        ).toFixed(2)}KiB.`,\n      );\n    }\n    this.historicalLocations.clear();\n\n    const result: GameStateDiff = {\n      world: { ...this.world.serialize(), historicalLocations },\n      agentOperations: this.pendingOperations,\n    };\n    this.pendingOperations = [];\n    if (this.descriptionsModified) {\n      result.playerDescriptions = serializeMap(this.playerDescriptions);\n      result.agentDescriptions = serializeMap(this.agentDescriptions);\n      result.worldMap = this.worldMap.serialize();\n      this.descriptionsModified = false;\n    }\n    return result;\n  }\n\n  static async saveDiff(ctx: MutationCtx, worldId: Id<'worlds'>, diff: GameStateDiff) {\n    const existingWorld = await ctx.db.get(worldId);\n    if (!existingWorld) {\n      throw new Error(`No world found with id ${worldId}`);\n    }\n    const newWorld = diff.world;\n    // Archive newly deleted players, conversations, and agents.\n    for (const player of existingWorld.players) {\n      if (!newWorld.players.some((p) => p.id === player.id)) {\n        await ctx.db.insert('archivedPlayers', { worldId, ...player });\n      }\n    }\n    for (const conversation of existingWorld.conversations) {\n      if (!newWorld.conversations.some((c) => c.id === conversation.id)) {\n        const participants = conversation.participants.map((p) => p.playerId);\n        const archivedConversation = {\n          worldId,\n          id: conversation.id,\n          created: conversation.created,\n          creator: conversation.creator,\n          ended: Date.now(),\n          lastMessage: conversation.lastMessage,\n          numMessages: conversation.numMessages,\n          participants,\n        };\n        await ctx.db.insert('archivedConversations', archivedConversation);\n        for (let i = 0; i < participants.length; i++) {\n          for (let j = 0; j < participants.length; j++) {\n            if (i == j) {\n              continue;\n            }\n            const player1 = participants[i];\n            const player2 = participants[j];\n            await ctx.db.insert('participatedTogether', {\n              worldId,\n              conversationId: conversation.id,\n              player1,\n              player2,\n              ended: Date.now(),\n            });\n          }\n        }\n      }\n    }\n    for (const conversation of existingWorld.agents) {\n      if (!newWorld.agents.some((a) => a.id === conversation.id)) {\n        await ctx.db.insert('archivedAgents', { worldId, ...conversation });\n      }\n    }\n    // Update the world state.\n    await ctx.db.replace(worldId, newWorld);\n\n    // Update the larger description tables if they changed.\n    const { playerDescriptions, agentDescriptions, worldMap } = diff;\n    if (playerDescriptions) {\n      for (const description of playerDescriptions) {\n        const existing = await ctx.db\n          .query('playerDescriptions')\n          .withIndex('worldId', (q) =>\n            q.eq('worldId', worldId).eq('playerId', description.playerId),\n          )\n          .unique();\n        if (existing) {\n          await ctx.db.replace(existing._id, { worldId, ...description });\n        } else {\n          await ctx.db.insert('playerDescriptions', { worldId, ...description });\n        }\n      }\n    }\n    if (agentDescriptions) {\n      for (const description of agentDescriptions) {\n        const existing = await ctx.db\n          .query('agentDescriptions')\n          .withIndex('worldId', (q) => q.eq('worldId', worldId).eq('agentId', description.agentId))\n          .unique();\n        if (existing) {\n          await ctx.db.replace(existing._id, { worldId, ...description });\n        } else {\n          await ctx.db.insert('agentDescriptions', { worldId, ...description });\n        }\n      }\n    }\n    if (worldMap) {\n      const existing = await ctx.db\n        .query('maps')\n        .withIndex('worldId', (q) => q.eq('worldId', worldId))\n        .unique();\n      if (existing) {\n        await ctx.db.replace(existing._id, { worldId, ...worldMap });\n      } else {\n        await ctx.db.insert('maps', { worldId, ...worldMap });\n      }\n    }\n    // Start the desired agent operations.\n    for (const operation of diff.agentOperations) {\n      await runAgentOperation(ctx, operation.name, operation.args);\n    }\n  }\n}\n\nexport const loadWorld = internalQuery({\n  args: {\n    worldId: v.id('worlds'),\n    generationNumber: v.number(),\n  },\n  handler: async (ctx, args) => {\n    return await Game.load(ctx.db, args.worldId, args.generationNumber);\n  },\n});\n\nexport const saveWorld = internalMutation({\n  args: {\n    engineId: v.id('engines'),\n    engineUpdate,\n    worldId: v.id('worlds'),\n    worldDiff: gameStateDiff,\n  },\n  handler: async (ctx, args) => {\n    await applyEngineUpdate(ctx, args.engineId, args.engineUpdate);\n    await Game.saveDiff(ctx, args.worldId, args.worldDiff);\n  },\n});\n"
  },
  {
    "path": "convex/aiTown/ids.ts",
    "content": "import { v } from 'convex/values';\n\nconst IdShortCodes = { agents: 'a', conversations: 'c', players: 'p', operations: 'o' };\nexport type IdTypes = keyof typeof IdShortCodes;\n\nexport type GameId<T extends IdTypes> = string & { __type: T };\n\nexport function parseGameId<T extends IdTypes>(idType: T, gameId: string): GameId<T> {\n  const type = gameId[0];\n  const match = Object.entries(IdShortCodes).find(([_, value]) => value === type);\n  if (!match || match[0] !== idType) {\n    throw new Error(`Invalid game ID type: ${type}`);\n  }\n  const number = parseInt(gameId.slice(2), 10);\n  if (isNaN(number) || !Number.isInteger(number) || number < 0) {\n    throw new Error(`Invalid game ID number: ${gameId}`);\n  }\n  return gameId as GameId<T>;\n}\n\nexport function allocGameId<T extends IdTypes>(idType: T, idNumber: number): GameId<T> {\n  const type = IdShortCodes[idType];\n  if (!type) {\n    throw new Error(`Invalid game ID type: ${idType}`);\n  }\n  return `${type}:${idNumber}` as GameId<T>;\n}\n\nexport const conversationId = v.string();\nexport const playerId = v.string();\nexport const agentId = v.string();\nexport const operationId = v.string();\n"
  },
  {
    "path": "convex/aiTown/inputHandler.ts",
    "content": "import { ObjectType, PropertyValidators, Value } from 'convex/values';\nimport type { Game } from './game';\n\nexport function inputHandler<ArgsValidator extends PropertyValidators, Return extends Value>(def: {\n  args: ArgsValidator;\n  handler: (game: Game, now: number, args: ObjectType<ArgsValidator>) => Return;\n}) {\n  return def;\n}\n"
  },
  {
    "path": "convex/aiTown/inputs.ts",
    "content": "import { ObjectType } from 'convex/values';\nimport { playerInputs } from './player';\nimport { conversationInputs } from './conversation';\nimport { agentInputs } from './agentInputs';\n\n// It's easy to hit circular dependencies with these imports,\n// so assert at module scope so we hit errors when analyzing.\nif (playerInputs === undefined || conversationInputs === undefined || agentInputs === undefined) {\n  throw new Error(\"Input map is undefined, check if there's a circular import.\");\n}\nexport const inputs = {\n  ...playerInputs,\n  // Inputs for the messaging layer.\n  ...conversationInputs,\n  // Inputs for the agent layer.\n  ...agentInputs,\n};\nexport type Inputs = typeof inputs;\nexport type InputNames = keyof Inputs;\nexport type InputArgs<Name extends InputNames> = ObjectType<Inputs[Name]['args']>;\nexport type InputReturnValue<Name extends InputNames> = ReturnType<\n  Inputs[Name]['handler']\n> extends Promise<infer T>\n  ? T\n  : never;\n"
  },
  {
    "path": "convex/aiTown/insertInput.ts",
    "content": "import { MutationCtx } from '../_generated/server';\nimport { Id } from '../_generated/dataModel';\nimport { engineInsertInput } from '../engine/abstractGame';\nimport { InputNames, InputArgs } from './inputs';\n\nexport async function insertInput<Name extends InputNames>(\n  ctx: MutationCtx,\n  worldId: Id<'worlds'>,\n  name: Name,\n  args: InputArgs<Name>,\n): Promise<Id<'inputs'>> {\n  const worldStatus = await ctx.db\n    .query('worldStatus')\n    .withIndex('worldId', (q) => q.eq('worldId', worldId))\n    .unique();\n  if (!worldStatus) {\n    throw new Error(`World for engine ${worldId} not found`);\n  }\n  return await engineInsertInput(ctx, worldStatus.engineId, name, args);\n}\n"
  },
  {
    "path": "convex/aiTown/location.ts",
    "content": "import { FieldConfig } from '../engine/historicalObject';\nimport { Player } from './player';\n\nexport type Location = {\n  // Unpacked player position.\n  x: number;\n  y: number;\n\n  // Normalized facing vector.\n  dx: number;\n  dy: number;\n\n  speed: number;\n};\n\nexport const locationFields: FieldConfig = [\n  { name: 'x', precision: 8 },\n  { name: 'y', precision: 8 },\n  { name: 'dx', precision: 8 },\n  { name: 'dy', precision: 8 },\n  { name: 'speed', precision: 16 },\n];\n\nexport function playerLocation(player: Player): Location {\n  return {\n    x: player.position.x,\n    y: player.position.y,\n    dx: player.facing.dx,\n    dy: player.facing.dy,\n    speed: player.speed,\n  };\n}\n"
  },
  {
    "path": "convex/aiTown/main.ts",
    "content": "import { ConvexError, v } from 'convex/values';\nimport { DatabaseReader, MutationCtx, internalAction, mutation, query } from '../_generated/server';\nimport { insertInput } from './insertInput';\nimport { Game } from './game';\nimport { internal } from '../_generated/api';\nimport { sleep } from '../util/sleep';\nimport { Id } from '../_generated/dataModel';\nimport { ENGINE_ACTION_DURATION } from '../constants';\n\nexport async function createEngine(ctx: MutationCtx) {\n  const now = Date.now();\n  const engineId = await ctx.db.insert('engines', {\n    currentTime: now,\n    generationNumber: 0,\n    running: true,\n  });\n  return engineId;\n}\n\nasync function loadWorldStatus(db: DatabaseReader, worldId: Id<'worlds'>) {\n  const worldStatus = await db\n    .query('worldStatus')\n    .withIndex('worldId', (q) => q.eq('worldId', worldId))\n    .unique();\n  if (!worldStatus) {\n    throw new Error(`No engine found for world ${worldId}`);\n  }\n  return worldStatus;\n}\n\nexport async function startEngine(ctx: MutationCtx, worldId: Id<'worlds'>) {\n  const { engineId } = await loadWorldStatus(ctx.db, worldId);\n  const engine = await ctx.db.get(engineId);\n  if (!engine) {\n    throw new Error(`Invalid engine ID: ${engineId}`);\n  }\n  if (engine.running) {\n    throw new Error(`Engine ${engineId} isn't currently stopped`);\n  }\n  const now = Date.now();\n  const generationNumber = engine.generationNumber + 1;\n  await ctx.db.patch(engineId, {\n    // Forcibly advance time to the present. This does mean we'll skip\n    // simulating the time the engine was stopped, but we don't want\n    // to have to simulate a potentially large stopped window and send\n    // it down to clients.\n    lastStepTs: engine.currentTime,\n    currentTime: now,\n    running: true,\n    generationNumber,\n  });\n  await ctx.scheduler.runAfter(0, internal.aiTown.main.runStep, {\n    worldId: worldId,\n    generationNumber,\n    maxDuration: ENGINE_ACTION_DURATION,\n  });\n}\n\nexport async function kickEngine(ctx: MutationCtx, worldId: Id<'worlds'>) {\n  const { engineId } = await loadWorldStatus(ctx.db, worldId);\n  const engine = await ctx.db.get(engineId);\n  if (!engine) {\n    throw new Error(`Invalid engine ID: ${engineId}`);\n  }\n  if (!engine.running) {\n    throw new Error(`Engine ${engineId} isn't currently running`);\n  }\n  const generationNumber = engine.generationNumber + 1;\n  await ctx.db.patch(engineId, { generationNumber });\n  await ctx.scheduler.runAfter(0, internal.aiTown.main.runStep, {\n    worldId: worldId,\n    generationNumber,\n    maxDuration: ENGINE_ACTION_DURATION,\n  });\n}\n\nexport async function stopEngine(ctx: MutationCtx, worldId: Id<'worlds'>) {\n  const { engineId } = await loadWorldStatus(ctx.db, worldId);\n  const engine = await ctx.db.get(engineId);\n  if (!engine) {\n    throw new Error(`Invalid engine ID: ${engineId}`);\n  }\n  if (!engine.running) {\n    throw new Error(`Engine ${engineId} isn't currently running`);\n  }\n  await ctx.db.patch(engineId, { running: false });\n}\n\nexport const runStep = internalAction({\n  args: {\n    worldId: v.id('worlds'),\n    generationNumber: v.number(),\n    maxDuration: v.number(),\n  },\n  handler: async (ctx, args) => {\n    try {\n      const { engine, gameState } = await ctx.runQuery(internal.aiTown.game.loadWorld, {\n        worldId: args.worldId,\n        generationNumber: args.generationNumber,\n      });\n      const game = new Game(engine, args.worldId, gameState);\n\n      let now = Date.now();\n      const deadline = now + args.maxDuration;\n      while (now < deadline) {\n        await game.runStep(ctx, now);\n        const sleepUntil = Math.min(now + game.stepDuration, deadline);\n        await sleep(sleepUntil - now);\n        now = Date.now();\n      }\n      await ctx.scheduler.runAfter(0, internal.aiTown.main.runStep, {\n        worldId: args.worldId,\n        generationNumber: game.engine.generationNumber,\n        maxDuration: args.maxDuration,\n      });\n    } catch (e: unknown) {\n      if (e instanceof ConvexError) {\n        if (e.data.kind === 'engineNotRunning') {\n          console.debug(`Engine is not running: ${e.message}`);\n          return;\n        }\n        if (e.data.kind === 'generationNumber') {\n          console.debug(`Generation number mismatch: ${e.message}`);\n          return;\n        }\n      }\n      throw e;\n    }\n  },\n});\n\nexport const sendInput = mutation({\n  args: {\n    worldId: v.id('worlds'),\n    name: v.string(),\n    args: v.any(),\n  },\n  handler: async (ctx, args) => {\n    return await insertInput(ctx, args.worldId, args.name as any, args.args);\n  },\n});\n\nexport const inputStatus = query({\n  args: {\n    inputId: v.id('inputs'),\n  },\n  handler: async (ctx, args) => {\n    const input = await ctx.db.get(args.inputId);\n    if (!input) {\n      throw new Error(`Invalid input ID: ${args.inputId}`);\n    }\n    return input.returnValue ?? null;\n  },\n});\n"
  },
  {
    "path": "convex/aiTown/movement.ts",
    "content": "import { movementSpeed } from '../../data/characters';\nimport { COLLISION_THRESHOLD } from '../constants';\nimport { compressPath, distance, manhattanDistance, pointsEqual } from '../util/geometry';\nimport { MinHeap } from '../util/minheap';\nimport { Point, Vector } from '../util/types';\nimport { Game } from './game';\nimport { GameId } from './ids';\nimport { Player } from './player';\nimport { WorldMap } from './worldMap';\n\ntype PathCandidate = {\n  position: Point;\n  facing?: Vector;\n  t: number;\n  length: number;\n  cost: number;\n  prev?: PathCandidate;\n};\n\nexport function stopPlayer(player: Player) {\n  delete player.pathfinding;\n  player.speed = 0;\n}\n\nexport function movePlayer(\n  game: Game,\n  now: number,\n  player: Player,\n  destination: Point,\n  allowInConversation?: boolean,\n) {\n  if (Math.floor(destination.x) !== destination.x || Math.floor(destination.y) !== destination.y) {\n    throw new Error(`Non-integral destination: ${JSON.stringify(destination)}`);\n  }\n  const { position } = player;\n  // Close enough to current position or destination => no-op.\n  if (pointsEqual(position, destination)) {\n    return;\n  }\n  // Don't allow players in a conversation to move.\n  const inConversation = [...game.world.conversations.values()].some(\n    (c) => c.participants.get(player.id)?.status.kind === 'participating',\n  );\n  if (inConversation && !allowInConversation) {\n    throw new Error(`Can't move when in a conversation. Leave the conversation first!`);\n  }\n  player.pathfinding = {\n    destination: destination,\n    started: now,\n    state: {\n      kind: 'needsPath',\n    },\n  };\n  return;\n}\n\nexport function findRoute(game: Game, now: number, player: Player, destination: Point) {\n  const minDistances: PathCandidate[][] = [];\n  const explore = (current: PathCandidate): Array<PathCandidate> => {\n    const { x, y } = current.position;\n    const neighbors = [];\n\n    // If we're not on a grid point, first try to move horizontally\n    // or vertically to a grid point. Note that this can create very small\n    // deltas between the current position and the nearest grid point so\n    // be careful to preserve the `facing` vectors rather than trying to\n    // derive them anew.\n    if (x !== Math.floor(x)) {\n      neighbors.push(\n        { position: { x: Math.floor(x), y }, facing: { dx: -1, dy: 0 } },\n        { position: { x: Math.floor(x) + 1, y }, facing: { dx: 1, dy: 0 } },\n      );\n    }\n    if (y !== Math.floor(y)) {\n      neighbors.push(\n        { position: { x, y: Math.floor(y) }, facing: { dx: 0, dy: -1 } },\n        { position: { x, y: Math.floor(y) + 1 }, facing: { dx: 0, dy: 1 } },\n      );\n    }\n    // Otherwise, just move to adjacent grid points.\n    if (x == Math.floor(x) && y == Math.floor(y)) {\n      neighbors.push(\n        { position: { x: x + 1, y }, facing: { dx: 1, dy: 0 } },\n        { position: { x: x - 1, y }, facing: { dx: -1, dy: 0 } },\n        { position: { x, y: y + 1 }, facing: { dx: 0, dy: 1 } },\n        { position: { x, y: y - 1 }, facing: { dx: 0, dy: -1 } },\n      );\n    }\n    const next = [];\n    for (const { position, facing } of neighbors) {\n      const segmentLength = distance(current.position, position);\n      const length = current.length + segmentLength;\n      if (blocked(game, now, position, player.id)) {\n        continue;\n      }\n      const remaining = manhattanDistance(position, destination);\n      const path = {\n        position,\n        facing,\n        // Movement speed is in tiles per second.\n        t: current.t + (segmentLength / movementSpeed) * 1000,\n        length,\n        cost: length + remaining,\n        prev: current,\n      };\n      const existingMin = minDistances[position.y]?.[position.x];\n      if (existingMin && existingMin.cost <= path.cost) {\n        continue;\n      }\n      minDistances[position.y] ??= [];\n      minDistances[position.y][position.x] = path;\n      next.push(path);\n    }\n    return next;\n  };\n\n  const startingLocation = player.position;\n  const startingPosition = { x: startingLocation.x, y: startingLocation.y };\n  let current: PathCandidate | undefined = {\n    position: startingPosition,\n    facing: player.facing,\n    t: now,\n    length: 0,\n    cost: manhattanDistance(startingPosition, destination),\n    prev: undefined,\n  };\n  let bestCandidate = current;\n  const minheap = MinHeap<PathCandidate>((p0, p1) => p0.cost > p1.cost);\n  while (current) {\n    if (pointsEqual(current.position, destination)) {\n      break;\n    }\n    if (\n      manhattanDistance(current.position, destination) <\n      manhattanDistance(bestCandidate.position, destination)\n    ) {\n      bestCandidate = current;\n    }\n    for (const candidate of explore(current)) {\n      minheap.push(candidate);\n    }\n    current = minheap.pop();\n  }\n  let newDestination = null;\n  if (!current) {\n    if (bestCandidate.length === 0) {\n      return null;\n    }\n    current = bestCandidate;\n    newDestination = current.position;\n  }\n  const densePath = [];\n  let facing = current.facing!;\n  while (current) {\n    densePath.push({ position: current.position, t: current.t, facing });\n    facing = current.facing!;\n    current = current.prev;\n  }\n  densePath.reverse();\n\n  return { path: compressPath(densePath), newDestination };\n}\n\nexport function blocked(game: Game, now: number, pos: Point, playerId?: GameId<'players'>) {\n  const otherPositions = [...game.world.players.values()]\n    .filter((p) => p.id !== playerId)\n    .map((p) => p.position);\n  return blockedWithPositions(pos, otherPositions, game.worldMap);\n}\n\nexport function blockedWithPositions(position: Point, otherPositions: Point[], map: WorldMap) {\n  if (isNaN(position.x) || isNaN(position.y)) {\n    throw new Error(`NaN position in ${JSON.stringify(position)}`);\n  }\n  if (position.x < 0 || position.y < 0 || position.x >= map.width || position.y >= map.height) {\n    return 'out of bounds';\n  }\n  for (const layer of map.objectTiles) {\n    if (layer[Math.floor(position.x)][Math.floor(position.y)] !== -1) {\n      return 'world blocked';\n    }\n  }\n  for (const otherPosition of otherPositions) {\n    if (distance(otherPosition, position) < COLLISION_THRESHOLD) {\n      return 'player';\n    }\n  }\n  return null;\n}\n"
  },
  {
    "path": "convex/aiTown/player.ts",
    "content": "import { Infer, ObjectType, v } from 'convex/values';\nimport { Point, Vector, path, point, vector } from '../util/types';\nimport { GameId, parseGameId } from './ids';\nimport { playerId } from './ids';\nimport {\n  PATHFINDING_TIMEOUT,\n  PATHFINDING_BACKOFF,\n  HUMAN_IDLE_TOO_LONG,\n  MAX_HUMAN_PLAYERS,\n  MAX_PATHFINDS_PER_STEP,\n} from '../constants';\nimport { pointsEqual, pathPosition } from '../util/geometry';\nimport { Game } from './game';\nimport { stopPlayer, findRoute, blocked, movePlayer } from './movement';\nimport { inputHandler } from './inputHandler';\nimport { characters } from '../../data/characters';\nimport { PlayerDescription } from './playerDescription';\n\nconst pathfinding = v.object({\n  destination: point,\n  started: v.number(),\n  state: v.union(\n    v.object({\n      kind: v.literal('needsPath'),\n    }),\n    v.object({\n      kind: v.literal('waiting'),\n      until: v.number(),\n    }),\n    v.object({\n      kind: v.literal('moving'),\n      path,\n    }),\n  ),\n});\nexport type Pathfinding = Infer<typeof pathfinding>;\n\nexport const activity = v.object({\n  description: v.string(),\n  emoji: v.optional(v.string()),\n  until: v.number(),\n});\nexport type Activity = Infer<typeof activity>;\n\nexport const serializedPlayer = {\n  id: playerId,\n  human: v.optional(v.string()),\n  pathfinding: v.optional(pathfinding),\n  activity: v.optional(activity),\n\n  // The last time they did something.\n  lastInput: v.number(),\n\n  position: point,\n  facing: vector,\n  speed: v.number(),\n};\nexport type SerializedPlayer = ObjectType<typeof serializedPlayer>;\n\nexport class Player {\n  id: GameId<'players'>;\n  human?: string;\n  pathfinding?: Pathfinding;\n  activity?: Activity;\n\n  lastInput: number;\n\n  position: Point;\n  facing: Vector;\n  speed: number;\n\n  constructor(serialized: SerializedPlayer) {\n    const { id, human, pathfinding, activity, lastInput, position, facing, speed } = serialized;\n    this.id = parseGameId('players', id);\n    this.human = human;\n    this.pathfinding = pathfinding;\n    this.activity = activity;\n    this.lastInput = lastInput;\n    this.position = position;\n    this.facing = facing;\n    this.speed = speed;\n  }\n\n  tick(game: Game, now: number) {\n    if (this.human && this.lastInput < now - HUMAN_IDLE_TOO_LONG) {\n      this.leave(game, now);\n    }\n  }\n\n  tickPathfinding(game: Game, now: number) {\n    // There's nothing to do if we're not moving.\n    const { pathfinding, position } = this;\n    if (!pathfinding) {\n      return;\n    }\n\n    // Stop pathfinding if we've reached our destination.\n    if (pathfinding.state.kind === 'moving' && pointsEqual(pathfinding.destination, position)) {\n      stopPlayer(this);\n    }\n\n    // Stop pathfinding if we've timed out.\n    if (pathfinding.started + PATHFINDING_TIMEOUT < now) {\n      console.warn(`Timing out pathfinding for ${this.id}`);\n      stopPlayer(this);\n    }\n\n    // Transition from \"waiting\" to \"needsPath\" if we're past the deadline.\n    if (pathfinding.state.kind === 'waiting' && pathfinding.state.until < now) {\n      pathfinding.state = { kind: 'needsPath' };\n    }\n\n    // Perform pathfinding if needed.\n    if (pathfinding.state.kind === 'needsPath' && game.numPathfinds < MAX_PATHFINDS_PER_STEP) {\n      game.numPathfinds++;\n      if (game.numPathfinds === MAX_PATHFINDS_PER_STEP) {\n        console.warn(`Reached max pathfinds for this step`);\n      }\n      const route = findRoute(game, now, this, pathfinding.destination);\n      if (route === null) {\n        console.log(`Failed to route to ${JSON.stringify(pathfinding.destination)}`);\n        stopPlayer(this);\n      } else {\n        if (route.newDestination) {\n          console.warn(\n            `Updating destination from ${JSON.stringify(\n              pathfinding.destination,\n            )} to ${JSON.stringify(route.newDestination)}`,\n          );\n          pathfinding.destination = route.newDestination;\n        }\n        pathfinding.state = { kind: 'moving', path: route.path };\n      }\n    }\n  }\n\n  tickPosition(game: Game, now: number) {\n    // There's nothing to do if we're not moving.\n    if (!this.pathfinding || this.pathfinding.state.kind !== 'moving') {\n      this.speed = 0;\n      return;\n    }\n\n    // Compute a candidate new position and check if it collides\n    // with anything.\n    const candidate = pathPosition(this.pathfinding.state.path as any, now);\n    if (!candidate) {\n      console.warn(`Path out of range of ${now} for ${this.id}`);\n      return;\n    }\n    const { position, facing, velocity } = candidate;\n    const collisionReason = blocked(game, now, position, this.id);\n    if (collisionReason !== null) {\n      const backoff = Math.random() * PATHFINDING_BACKOFF;\n      console.warn(`Stopping path for ${this.id}, waiting for ${backoff}ms: ${collisionReason}`);\n      this.pathfinding.state = {\n        kind: 'waiting',\n        until: now + backoff,\n      };\n      return;\n    }\n    // Update the player's location.\n    this.position = position;\n    this.facing = facing;\n    this.speed = velocity;\n  }\n\n  static join(\n    game: Game,\n    now: number,\n    name: string,\n    character: string,\n    description: string,\n    tokenIdentifier?: string,\n  ) {\n    if (tokenIdentifier) {\n      let numHumans = 0;\n      for (const player of game.world.players.values()) {\n        if (player.human) {\n          numHumans++;\n        }\n        if (player.human === tokenIdentifier) {\n          throw new Error(`You are already in this game!`);\n        }\n      }\n      if (numHumans >= MAX_HUMAN_PLAYERS) {\n        throw new Error(`Only ${MAX_HUMAN_PLAYERS} human players allowed at once.`);\n      }\n    }\n    let position;\n    for (let attempt = 0; attempt < 10; attempt++) {\n      const candidate = {\n        x: Math.floor(Math.random() * game.worldMap.width),\n        y: Math.floor(Math.random() * game.worldMap.height),\n      };\n      if (blocked(game, now, candidate)) {\n        continue;\n      }\n      position = candidate;\n      break;\n    }\n    if (!position) {\n      throw new Error(`Failed to find a free position!`);\n    }\n    const facingOptions = [\n      { dx: 1, dy: 0 },\n      { dx: -1, dy: 0 },\n      { dx: 0, dy: 1 },\n      { dx: 0, dy: -1 },\n    ];\n    const facing = facingOptions[Math.floor(Math.random() * facingOptions.length)];\n    if (!characters.find((c) => c.name === character)) {\n      throw new Error(`Invalid character: ${character}`);\n    }\n    const playerId = game.allocId('players');\n    game.world.players.set(\n      playerId,\n      new Player({\n        id: playerId,\n        human: tokenIdentifier,\n        lastInput: now,\n        position,\n        facing,\n        speed: 0,\n      }),\n    );\n    game.playerDescriptions.set(\n      playerId,\n      new PlayerDescription({\n        playerId,\n        character,\n        description,\n        name,\n      }),\n    );\n    game.descriptionsModified = true;\n    return playerId;\n  }\n\n  leave(game: Game, now: number) {\n    // Stop our conversation if we're leaving the game.\n    const conversation = [...game.world.conversations.values()].find((c) =>\n      c.participants.has(this.id),\n    );\n    if (conversation) {\n      conversation.stop(game, now);\n    }\n    game.world.players.delete(this.id);\n  }\n\n  serialize(): SerializedPlayer {\n    const { id, human, pathfinding, activity, lastInput, position, facing, speed } = this;\n    return {\n      id,\n      human,\n      pathfinding,\n      activity,\n      lastInput,\n      position,\n      facing,\n      speed,\n    };\n  }\n}\n\nexport const playerInputs = {\n  join: inputHandler({\n    args: {\n      name: v.string(),\n      character: v.string(),\n      description: v.string(),\n      tokenIdentifier: v.optional(v.string()),\n    },\n    handler: (game, now, args) => {\n      Player.join(game, now, args.name, args.character, args.description, args.tokenIdentifier);\n      return null;\n    },\n  }),\n  leave: inputHandler({\n    args: { playerId },\n    handler: (game, now, args) => {\n      const playerId = parseGameId('players', args.playerId);\n      const player = game.world.players.get(playerId);\n      if (!player) {\n        throw new Error(`Invalid player ID ${playerId}`);\n      }\n      player.leave(game, now);\n      return null;\n    },\n  }),\n  moveTo: inputHandler({\n    args: {\n      playerId,\n      destination: v.union(point, v.null()),\n    },\n    handler: (game, now, args) => {\n      const playerId = parseGameId('players', args.playerId);\n      const player = game.world.players.get(playerId);\n      if (!player) {\n        throw new Error(`Invalid player ID ${playerId}`);\n      }\n      if (args.destination) {\n        movePlayer(game, now, player, args.destination);\n      } else {\n        stopPlayer(player);\n      }\n      return null;\n    },\n  }),\n};\n"
  },
  {
    "path": "convex/aiTown/playerDescription.ts",
    "content": "import { ObjectType, v } from 'convex/values';\nimport { GameId, parseGameId, playerId } from './ids';\n\nexport const serializedPlayerDescription = {\n  playerId,\n  name: v.string(),\n  description: v.string(),\n  character: v.string(),\n};\nexport type SerializedPlayerDescription = ObjectType<typeof serializedPlayerDescription>;\n\nexport class PlayerDescription {\n  playerId: GameId<'players'>;\n  name: string;\n  description: string;\n  character: string;\n\n  constructor(serialized: SerializedPlayerDescription) {\n    const { playerId, name, description, character } = serialized;\n    this.playerId = parseGameId('players', playerId);\n    this.name = name;\n    this.description = description;\n    this.character = character;\n  }\n\n  serialize(): SerializedPlayerDescription {\n    const { playerId, name, description, character } = this;\n    return {\n      playerId,\n      name,\n      description,\n      character,\n    };\n  }\n}\n"
  },
  {
    "path": "convex/aiTown/schema.ts",
    "content": "import { v } from 'convex/values';\nimport { defineTable } from 'convex/server';\nimport { serializedPlayer } from './player';\nimport { serializedPlayerDescription } from './playerDescription';\nimport { serializedAgent } from './agent';\nimport { serializedAgentDescription } from './agentDescription';\nimport { serializedWorld } from './world';\nimport { serializedWorldMap } from './worldMap';\nimport { serializedConversation } from './conversation';\nimport { conversationId, playerId } from './ids';\n\nexport const aiTownTables = {\n  // This table has a single document that stores all players, conversations, and agents. This\n  // data is small and changes regularly over time.\n  worlds: defineTable({ ...serializedWorld }),\n\n  // Worlds can be started or stopped by the developer or paused for inactivity, and this\n  // infrequently changing document tracks this world state.\n  worldStatus: defineTable({\n    worldId: v.id('worlds'),\n    isDefault: v.boolean(),\n    engineId: v.id('engines'),\n    lastViewed: v.number(),\n    status: v.union(v.literal('running'), v.literal('stoppedByDeveloper'), v.literal('inactive')),\n  }).index('worldId', ['worldId']),\n\n  // This table contains the map data for a given world. Since it's a bit larger than the player\n  // state and infrequently changes, we store it in a separate table.\n  maps: defineTable({\n    worldId: v.id('worlds'),\n    ...serializedWorldMap,\n  }).index('worldId', ['worldId']),\n\n  // Human readable text describing players and agents that's stored in separate tables, just like `maps`.\n  playerDescriptions: defineTable({\n    worldId: v.id('worlds'),\n    ...serializedPlayerDescription,\n  }).index('worldId', ['worldId', 'playerId']),\n  agentDescriptions: defineTable({\n    worldId: v.id('worlds'),\n    ...serializedAgentDescription,\n  }).index('worldId', ['worldId', 'agentId']),\n\n  //The game engine doesn't want to track players that have left or conversations that are over, since\n  // it wants to keep its managed state small. However, we may want to look at old conversations in the\n  // UI or from the agent code. So, whenever we delete an entry from within the world's document, we\n  // \"archive\" it within these tables.\n  archivedPlayers: defineTable({ worldId: v.id('worlds'), ...serializedPlayer }).index('worldId', [\n    'worldId',\n    'id',\n  ]),\n  archivedConversations: defineTable({\n    worldId: v.id('worlds'),\n    id: conversationId,\n    creator: playerId,\n    created: v.number(),\n    ended: v.number(),\n    lastMessage: serializedConversation.lastMessage,\n    numMessages: serializedConversation.numMessages,\n    participants: v.array(playerId),\n  }).index('worldId', ['worldId', 'id']),\n  archivedAgents: defineTable({ worldId: v.id('worlds'), ...serializedAgent }).index('worldId', [\n    'worldId',\n    'id',\n  ]),\n\n  // The agent layer wants to know what the last (completed) conversation was between two players,\n  // so this table represents a labelled graph indicating which players have talked to each other.\n  participatedTogether: defineTable({\n    worldId: v.id('worlds'),\n    conversationId,\n    player1: playerId,\n    player2: playerId,\n    ended: v.number(),\n  })\n    .index('edge', ['worldId', 'player1', 'player2', 'ended'])\n    .index('conversation', ['worldId', 'player1', 'conversationId'])\n    .index('playerHistory', ['worldId', 'player1', 'ended']),\n};\n"
  },
  {
    "path": "convex/aiTown/world.ts",
    "content": "import { ObjectType, v } from 'convex/values';\nimport { Conversation, serializedConversation } from './conversation';\nimport { Player, serializedPlayer } from './player';\nimport { Agent, serializedAgent } from './agent';\nimport { GameId, parseGameId, playerId } from './ids';\nimport { parseMap } from '../util/object';\n\nexport const historicalLocations = v.array(\n  v.object({\n    playerId,\n    location: v.bytes(),\n  }),\n);\n\nexport const serializedWorld = {\n  nextId: v.number(),\n  conversations: v.array(v.object(serializedConversation)),\n  players: v.array(v.object(serializedPlayer)),\n  agents: v.array(v.object(serializedAgent)),\n  historicalLocations: v.optional(historicalLocations),\n};\nexport type SerializedWorld = ObjectType<typeof serializedWorld>;\n\nexport class World {\n  nextId: number;\n  conversations: Map<GameId<'conversations'>, Conversation>;\n  players: Map<GameId<'players'>, Player>;\n  agents: Map<GameId<'agents'>, Agent>;\n  historicalLocations?: Map<GameId<'players'>, ArrayBuffer>;\n\n  constructor(serialized: SerializedWorld) {\n    const { nextId, historicalLocations } = serialized;\n\n    this.nextId = nextId;\n    this.conversations = parseMap(serialized.conversations, Conversation, (c) => c.id);\n    this.players = parseMap(serialized.players, Player, (p) => p.id);\n    this.agents = parseMap(serialized.agents, Agent, (a) => a.id);\n\n    if (historicalLocations) {\n      this.historicalLocations = new Map();\n      for (const { playerId, location } of historicalLocations) {\n        this.historicalLocations.set(parseGameId('players', playerId), location);\n      }\n    }\n  }\n\n  playerConversation(player: Player): Conversation | undefined {\n    return [...this.conversations.values()].find((c) => c.participants.has(player.id));\n  }\n\n  serialize(): SerializedWorld {\n    return {\n      nextId: this.nextId,\n      conversations: [...this.conversations.values()].map((c) => c.serialize()),\n      players: [...this.players.values()].map((p) => p.serialize()),\n      agents: [...this.agents.values()].map((a) => a.serialize()),\n      historicalLocations:\n        this.historicalLocations &&\n        [...this.historicalLocations.entries()].map(([playerId, location]) => ({\n          playerId,\n          location,\n        })),\n    };\n  }\n}\n"
  },
  {
    "path": "convex/aiTown/worldMap.ts",
    "content": "import { Infer, ObjectType, v } from 'convex/values';\n\n// `layer[position.x][position.y]` is the tileIndex or -1 if empty.\nconst tileLayer = v.array(v.array(v.number()));\nexport type TileLayer = Infer<typeof tileLayer>;\n\nconst animatedSprite = {\n  x: v.number(),\n  y: v.number(),\n  w: v.number(),\n  h: v.number(),\n  layer: v.number(),\n  sheet: v.string(),\n  animation: v.string(),\n};\nexport type AnimatedSprite = ObjectType<typeof animatedSprite>;\n\nexport const serializedWorldMap = {\n  width: v.number(),\n  height: v.number(),\n\n  tileSetUrl: v.string(),\n  //  Width & height of tileset image, px.\n  tileSetDimX: v.number(),\n  tileSetDimY: v.number(),\n\n  // Tile size in pixels (assume square)\n  tileDim: v.number(),\n  bgTiles: v.array(v.array(v.array(v.number()))),\n  objectTiles: v.array(tileLayer),\n  animatedSprites: v.array(v.object(animatedSprite)),\n};\nexport type SerializedWorldMap = ObjectType<typeof serializedWorldMap>;\n\nexport class WorldMap {\n  width: number;\n  height: number;\n\n  tileSetUrl: string;\n  tileSetDimX: number;\n  tileSetDimY: number;\n\n  tileDim: number;\n\n  bgTiles: TileLayer[];\n  objectTiles: TileLayer[];\n  animatedSprites: AnimatedSprite[];\n\n  constructor(serialized: SerializedWorldMap) {\n    this.width = serialized.width;\n    this.height = serialized.height;\n    this.tileSetUrl = serialized.tileSetUrl;\n    this.tileSetDimX = serialized.tileSetDimX;\n    this.tileSetDimY = serialized.tileSetDimY;\n    this.tileDim = serialized.tileDim;\n    this.bgTiles = serialized.bgTiles;\n    this.objectTiles = serialized.objectTiles;\n    this.animatedSprites = serialized.animatedSprites;\n  }\n\n  serialize(): SerializedWorldMap {\n    return {\n      width: this.width,\n      height: this.height,\n      tileSetUrl: this.tileSetUrl,\n      tileSetDimX: this.tileSetDimX,\n      tileSetDimY: this.tileSetDimY,\n      tileDim: this.tileDim,\n      bgTiles: this.bgTiles,\n      objectTiles: this.objectTiles,\n      animatedSprites: this.animatedSprites,\n    };\n  }\n}\n"
  },
  {
    "path": "convex/constants.ts",
    "content": "export const ACTION_TIMEOUT = 120_000; // more time for local dev\n// export const ACTION_TIMEOUT = 60_000;// normally fine\n\nexport const IDLE_WORLD_TIMEOUT = 5 * 60 * 1000;\nexport const WORLD_HEARTBEAT_INTERVAL = 60 * 1000;\n\nexport const MAX_STEP = 10 * 60 * 1000;\nexport const TICK = 16;\nexport const STEP_INTERVAL = 1000;\n\nexport const PATHFINDING_TIMEOUT = 60 * 1000;\nexport const PATHFINDING_BACKOFF = 1000;\nexport const CONVERSATION_DISTANCE = 1.3;\nexport const MIDPOINT_THRESHOLD = 4;\nexport const TYPING_TIMEOUT = 15 * 1000;\nexport const COLLISION_THRESHOLD = 0.75;\n\n// How many human players can be in a world at once.\nexport const MAX_HUMAN_PLAYERS = 8;\n\n// Don't talk to anyone for 15s after having a conversation.\nexport const CONVERSATION_COOLDOWN = 15000;\n\n// Don't do another activity for 10s after doing one.\nexport const ACTIVITY_COOLDOWN = 10_000;\n\n// Don't talk to a player within 60s of talking to them.\nexport const PLAYER_CONVERSATION_COOLDOWN = 60000;\n\n// Invite 80% of invites that come from other agents.\nexport const INVITE_ACCEPT_PROBABILITY = 0.8;\n\n// Wait for 1m for invites to be accepted.\nexport const INVITE_TIMEOUT = 60000;\n\n// Wait for another player to say something before jumping in.\nexport const AWKWARD_CONVERSATION_TIMEOUT = 60_000; // more time locally\n// export const AWKWARD_CONVERSATION_TIMEOUT = 20_000;\n\n// Leave a conversation after participating too long.\nexport const MAX_CONVERSATION_DURATION = 10 * 60_000; // more time locally\n// export const MAX_CONVERSATION_DURATION = 2 * 60_000;\n\n// Leave a conversation if it has more than 8 messages;\nexport const MAX_CONVERSATION_MESSAGES = 8;\n\n// Wait for 1s after sending an input to the engine. We can remove this\n// once we can await on an input being processed.\nexport const INPUT_DELAY = 1000;\n\n// How many memories to get from the agent's memory.\n// This is over-fetched by 10x so we can prioritize memories by more than relevance.\nexport const NUM_MEMORIES_TO_SEARCH = 3;\n\n// Wait for at least two seconds before sending another message.\nexport const MESSAGE_COOLDOWN = 2000;\n\n// Don't run a turn of the agent more than once a second.\nexport const AGENT_WAKEUP_THRESHOLD = 1000;\n\n// How old we let memories be before we vacuum them\nexport const VACUUM_MAX_AGE = 2 * 7 * 24 * 60 * 60 * 1000;\nexport const DELETE_BATCH_SIZE = 64;\n\nexport const HUMAN_IDLE_TOO_LONG = 5 * 60 * 1000;\n\nexport const ACTIVITIES = [\n  { description: 'reading a book', emoji: '📖', duration: 60_000 },\n  { description: 'daydreaming', emoji: '🤔', duration: 60_000 },\n  { description: 'gardening', emoji: '🥕', duration: 60_000 },\n];\n\nexport const ENGINE_ACTION_DURATION = 30000;\n\n// Bound the number of pathfinding searches we do per game step.\nexport const MAX_PATHFINDS_PER_STEP = 16;\n\nexport const DEFAULT_NAME = 'Me';\n"
  },
  {
    "path": "convex/crons.ts",
    "content": "import { cronJobs } from 'convex/server';\nimport { DELETE_BATCH_SIZE, IDLE_WORLD_TIMEOUT, VACUUM_MAX_AGE } from './constants';\nimport { internal } from './_generated/api';\nimport { internalMutation } from './_generated/server';\nimport { TableNames } from './_generated/dataModel';\nimport { v } from 'convex/values';\n\nconst crons = cronJobs();\n\ncrons.interval(\n  'stop inactive worlds',\n  { seconds: IDLE_WORLD_TIMEOUT / 1000 },\n  internal.world.stopInactiveWorlds,\n);\n\ncrons.interval('restart dead worlds', { seconds: 60 }, internal.world.restartDeadWorlds);\n\ncrons.daily('vacuum old entries', { hourUTC: 4, minuteUTC: 20 }, internal.crons.vacuumOldEntries);\n\nexport default crons;\n\nconst TablesToVacuum: TableNames[] = [\n  // Un-comment this to also clean out old conversations.\n  // 'conversationMembers', 'conversations', 'messages',\n\n  // Inputs aren't useful unless you're trying to replay history.\n  // If you want to support that, you should add a snapshot table, so you can\n  // replay from a certain time period. Or stop vacuuming inputs and replay from\n  // the beginning of time\n  'inputs',\n\n  // We can keep memories without their embeddings for inspection, but we won't\n  // retrieve them when searching memories via vector search.\n  'memories',\n  // We can vacuum fewer tables without serious consequences, but the only\n  // one that will cause issues over time is having >>100k vectors.\n  'memoryEmbeddings',\n];\n\nexport const vacuumOldEntries = internalMutation({\n  args: {},\n  handler: async (ctx, args) => {\n    const before = Date.now() - VACUUM_MAX_AGE;\n    for (const tableName of TablesToVacuum) {\n      console.log(`Checking ${tableName}...`);\n      const exists = await ctx.db\n        .query(tableName)\n        .withIndex('by_creation_time', (q) => q.lt('_creationTime', before))\n        .first();\n      if (exists) {\n        console.log(`Vacuuming ${tableName}...`);\n        await ctx.scheduler.runAfter(0, internal.crons.vacuumTable, {\n          tableName,\n          before,\n          cursor: null,\n          soFar: 0,\n        });\n      }\n    }\n  },\n});\n\nexport const vacuumTable = internalMutation({\n  args: {\n    tableName: v.string(),\n    before: v.number(),\n    cursor: v.union(v.string(), v.null()),\n    soFar: v.number(),\n  },\n  handler: async (ctx, { tableName, before, cursor, soFar }) => {\n    const results = await ctx.db\n      .query(tableName as TableNames)\n      .withIndex('by_creation_time', (q) => q.lt('_creationTime', before))\n      .paginate({ cursor, numItems: DELETE_BATCH_SIZE });\n    for (const row of results.page) {\n      await ctx.db.delete(row._id);\n    }\n    if (!results.isDone) {\n      await ctx.scheduler.runAfter(0, internal.crons.vacuumTable, {\n        tableName,\n        before,\n        soFar: results.page.length + soFar,\n        cursor: results.continueCursor,\n      });\n    } else {\n      console.log(`Vacuumed ${soFar + results.page.length} entries from ${tableName}`);\n    }\n  },\n});\n"
  },
  {
    "path": "convex/engine/abstractGame.ts",
    "content": "import { ConvexError, Infer, Value, v } from 'convex/values';\nimport { Doc, Id } from '../_generated/dataModel';\nimport { ActionCtx, DatabaseReader, MutationCtx, internalQuery } from '../_generated/server';\nimport { engine } from '../engine/schema';\nimport { internal } from '../_generated/api';\n\nexport abstract class AbstractGame {\n  abstract tickDuration: number;\n  abstract stepDuration: number;\n  abstract maxTicksPerStep: number;\n  abstract maxInputsPerStep: number;\n\n  constructor(public engine: Doc<'engines'>) {}\n\n  abstract handleInput(now: number, name: string, args: object): Value;\n  abstract tick(now: number): void;\n\n  // Optional callback at the beginning of each step.\n  beginStep(now: number) {}\n  abstract saveStep(ctx: ActionCtx, engineUpdate: EngineUpdate): Promise<void>;\n\n  async runStep(ctx: ActionCtx, now: number) {\n    const inputs = await ctx.runQuery(internal.engine.abstractGame.loadInputs, {\n      engineId: this.engine._id,\n      processedInputNumber: this.engine.processedInputNumber,\n      max: this.maxInputsPerStep,\n    });\n\n    const lastStepTs = this.engine.currentTime;\n    const startTs = lastStepTs ? lastStepTs + this.tickDuration : now;\n    let currentTs = startTs;\n    let inputIndex = 0;\n    let numTicks = 0;\n    let processedInputNumber = this.engine.processedInputNumber;\n    const completedInputs = [];\n\n    this.beginStep(currentTs);\n\n    while (numTicks < this.maxTicksPerStep) {\n      numTicks += 1;\n\n      // Collect all of the inputs for this tick.\n      const tickInputs = [];\n      while (inputIndex < inputs.length) {\n        const input = inputs[inputIndex];\n        if (input.received > currentTs) {\n          break;\n        }\n        inputIndex += 1;\n        processedInputNumber = input.number;\n        tickInputs.push(input);\n      }\n\n      // Feed the inputs to the game.\n      for (const input of tickInputs) {\n        let returnValue;\n        try {\n          const value = this.handleInput(currentTs, input.name, input.args);\n          returnValue = { kind: 'ok' as const, value };\n        } catch (e: any) {\n          console.error(`Input ${input._id} failed: ${e.message}`);\n          returnValue = { kind: 'error' as const, message: e.message };\n        }\n        completedInputs.push({ inputId: input._id, returnValue });\n      }\n\n      // Simulate the game forward one tick.\n      this.tick(currentTs);\n\n      const candidateTs = currentTs + this.tickDuration;\n      if (now < candidateTs) {\n        break;\n      }\n      currentTs = candidateTs;\n    }\n\n    // Commit the step by moving time forward, consuming our inputs, and saving the game's state.\n    const expectedGenerationNumber = this.engine.generationNumber;\n    this.engine.currentTime = currentTs;\n    this.engine.lastStepTs = lastStepTs;\n    this.engine.generationNumber += 1;\n    this.engine.processedInputNumber = processedInputNumber;\n    const { _id, _creationTime, ...engine } = this.engine;\n    const engineUpdate = { engine, completedInputs, expectedGenerationNumber };\n    await this.saveStep(ctx, engineUpdate);\n\n    console.debug(`Simulated from ${startTs} to ${currentTs} (${currentTs - startTs}ms)`);\n  }\n}\n\nconst completedInput = v.object({\n  inputId: v.id('inputs'),\n  returnValue: v.union(\n    v.object({\n      kind: v.literal('ok'),\n      value: v.any(),\n    }),\n    v.object({\n      kind: v.literal('error'),\n      message: v.string(),\n    }),\n  ),\n});\n\nexport const engineUpdate = v.object({\n  engine,\n  expectedGenerationNumber: v.number(),\n  completedInputs: v.array(completedInput),\n});\nexport type EngineUpdate = Infer<typeof engineUpdate>;\n\nexport async function loadEngine(\n  db: DatabaseReader,\n  engineId: Id<'engines'>,\n  generationNumber: number,\n) {\n  const engine = await db.get(engineId);\n  if (!engine) {\n    throw new Error(`No engine found with id ${engineId}`);\n  }\n  if (!engine.running) {\n    throw new ConvexError({\n      kind: 'engineNotRunning',\n      message: `Engine ${engineId} is not running`,\n    });\n  }\n  if (engine.generationNumber !== generationNumber) {\n    throw new ConvexError({ kind: 'generationNumber', message: 'Generation number mismatch' });\n  }\n  return engine;\n}\n\nexport async function engineInsertInput(\n  ctx: MutationCtx,\n  engineId: Id<'engines'>,\n  name: string,\n  args: any,\n): Promise<Id<'inputs'>> {\n  const now = Date.now();\n  const prevInput = await ctx.db\n    .query('inputs')\n    .withIndex('byInputNumber', (q) => q.eq('engineId', engineId))\n    .order('desc')\n    .first();\n  const number = prevInput ? prevInput.number + 1 : 0;\n  const inputId = await ctx.db.insert('inputs', {\n    engineId,\n    number,\n    name,\n    args,\n    received: now,\n  });\n  return inputId;\n}\n\nexport const loadInputs = internalQuery({\n  args: {\n    engineId: v.id('engines'),\n    processedInputNumber: v.optional(v.number()),\n    max: v.number(),\n  },\n  handler: async (ctx, args) => {\n    return await ctx.db\n      .query('inputs')\n      .withIndex('byInputNumber', (q) =>\n        q.eq('engineId', args.engineId).gt('number', args.processedInputNumber ?? -1),\n      )\n      .order('asc')\n      .take(args.max);\n  },\n});\n\nexport async function applyEngineUpdate(\n  ctx: MutationCtx,\n  engineId: Id<'engines'>,\n  update: EngineUpdate,\n) {\n  const engine = await loadEngine(ctx.db, engineId, update.expectedGenerationNumber);\n  if (\n    engine.currentTime &&\n    update.engine.currentTime &&\n    update.engine.currentTime < engine.currentTime\n  ) {\n    throw new Error('Time moving backwards');\n  }\n  await ctx.db.replace(engine._id, update.engine);\n\n  for (const completedInput of update.completedInputs) {\n    const input = await ctx.db.get(completedInput.inputId);\n    if (!input) {\n      throw new Error(`Input ${completedInput.inputId} not found`);\n    }\n    if (input.returnValue) {\n      throw new Error(`Input ${completedInput.inputId} already completed`);\n    }\n    input.returnValue = completedInput.returnValue;\n    await ctx.db.replace(input._id, input);\n  }\n}\n"
  },
  {
    "path": "convex/engine/historicalObject.test.ts",
    "content": "import { History, packSampleRecord, unpackSampleRecord } from './historicalObject';\n\ndescribe('HistoricalObject', () => {\n  test('pack sample record roundtrips', () => {\n    let data: Record<string, History> = {\n      x: {\n        initialValue: 0,\n        samples: [\n          { time: 1696021246740, value: 1 },\n          { time: 1696021246756, value: 2 },\n          { time: 1696021246772, value: 3 },\n          { time: 1696021246788, value: 4 },\n        ],\n      },\n      y: {\n        initialValue: 140.2,\n        samples: [\n          { time: 1696021246740, value: 169.7 },\n          { time: 1696021246756, value: 237.59 },\n          { time: 1696021246772, value: 344.44 },\n          { time: 1696021246788, value: 489.13 },\n        ],\n      },\n    };\n    const fields = [\n      { name: 'x', precision: 4 },\n      { name: 'y', precision: 4 },\n    ];\n    const packed = packSampleRecord(fields, data);\n    const unpacked = unpackSampleRecord(fields, packed);\n    const maxError = Math.max(1 / (1 << 4), 1e-8);\n\n    expect(Object.keys(data)).toEqual(Object.keys(unpacked));\n    for (const key of Object.keys(data)) {\n      const { initialValue, samples } = data[key];\n      const { initialValue: unpackedInitialValue, samples: unpackedSamples } = unpacked[key];\n      expect(Math.abs(initialValue - unpackedInitialValue)).toBeLessThanOrEqual(maxError);\n      expect(samples.length).toEqual(unpackedSamples.length);\n      for (let i = 0; i < samples.length; i++) {\n        const sample = samples[i];\n        const unpackedSample = unpackedSamples[i];\n        expect(sample.time).toEqual(unpackedSample.time);\n        expect(Math.abs(sample.value - unpackedSample.value)).toBeLessThanOrEqual(maxError);\n      }\n    }\n  });\n});\n"
  },
  {
    "path": "convex/engine/historicalObject.ts",
    "content": "import { xxHash32 } from '../util/xxhash';\nimport { compressSigned, uncompressSigned } from '../util/FastIntegerCompression';\nimport {\n  runLengthEncode,\n  deltaEncode,\n  quantize,\n  deltaDecode,\n  runLengthDecode,\n  unquantize,\n} from '../util/compression';\n\n// `HistoricalObject`s require the developer to pass in the\n// field names that'll be tracked and sent down to the client.\n//\n// By default, the historical tracking will round each floating point\n// value to an integer. The developer can specify more or less precision\n// via the `precision` parameter: the table's quantization will maintain\n// less than `1 / 2^precision` error. Note that higher precision values\n// imply less error.\nexport type FieldConfig = Array<string | { name: string; precision: number }>;\n\n// `HistoricalObject`s support at most 16 fields.\nconst MAX_FIELDS = 16;\n\nconst PACKED_VERSION = 1;\n\ntype NormalizedFieldConfig = Array<{\n  name: string;\n  precision: number;\n}>;\n\n// The `History` structure represents the history of a continuous\n// value over all bounded time. Each sample represents a line\n// segment that's extends to the previous sample's time inclusively\n// and to the sample's time non-inclusively. We track an `initialValue`\n// that goes to `-\\infty` up until the first sample, and the final\n// sample persists out to `+\\infty`.\n// ```\n//                    ^\n//                 position\n//                    |\n// samples[0].value - |         x---------------o\n//                    |\n// samples[1].value - |                         x-------->\n//                    |\n// initialValue     - <---------o\n//                    |\n//                     ------------------------------> time\n//                              |               |\n//                      samples[0].time  samples[1].time\n// ```\nexport type History = {\n  initialValue: number;\n  samples: Sample[];\n};\n\nexport type Sample = {\n  time: number;\n  value: number;\n};\n\n// `HistoricalObject` tracks a set of numeric fields over time and\n// supports compressing the fields' histories into a binary buffer.\n// This can be useful for continuous properties like position, where\n// we'd want to smoothly replay their tick-by-tick progress at a high\n// frame rate on the client.\n//\n// `HistoricalObject`s have a few limitations:\n// - Documents in a historical can only have up to 16 fields.\n// - The historical tracking only applies to a specified list of fields,\n//   and these fields must match between the client and server.\nexport class HistoricalObject<T extends Record<string, number>> {\n  startTs?: number;\n\n  fieldConfig: NormalizedFieldConfig;\n\n  data: T;\n  history: Record<string, History> = {};\n\n  constructor(fields: FieldConfig, initialValue: T) {\n    if (fields.length >= MAX_FIELDS) {\n      throw new Error(`HistoricalObject can have at most ${MAX_FIELDS} fields.`);\n    }\n    this.fieldConfig = normalizeFieldConfig(fields);\n    this.checkShape(initialValue);\n    this.data = initialValue;\n  }\n\n  historyLength() {\n    return Object.values(this.history)\n      .map((h) => h.samples.length)\n      .reduce((a, b) => a + b, 0);\n  }\n\n  checkShape(data: any) {\n    for (const [key, value] of Object.entries(data)) {\n      if (!this.fieldConfig.find((f) => f.name === key)) {\n        throw new Error(`Cannot set undeclared field '${key}'`);\n      }\n      if (typeof value !== 'number') {\n        throw new Error(\n          `HistoricalObject only supports numeric values, found: ${JSON.stringify(value)}`,\n        );\n      }\n    }\n  }\n\n  update(now: number, data: T) {\n    this.checkShape(data);\n    for (const [key, value] of Object.entries(data)) {\n      const currentValue = this.data[key];\n      if (currentValue !== value) {\n        let history = this.history[key];\n        if (!history) {\n          this.history[key] = history = { initialValue: currentValue, samples: [] };\n        }\n        const { samples } = history;\n        let inserted = false;\n        if (samples.length > 0) {\n          const last = samples[samples.length - 1];\n          if (now < last.time) {\n            throw new Error(`Server time moving backwards: ${now} < ${last.time}`);\n          }\n          if (now === last.time) {\n            last.value = value;\n            inserted = true;\n          }\n        }\n        if (!inserted) {\n          samples.push({ time: now, value });\n        }\n      }\n    }\n    this.data = data;\n  }\n\n  pack(): ArrayBuffer | null {\n    if (this.historyLength() === 0) {\n      return null;\n    }\n    return packSampleRecord(this.fieldConfig, this.history);\n  }\n}\n\n// Pack (normalized) field configuration into a binary buffer.\n//\n// Format:\n// ```\n// [ u8 version ]\n// for each field config:\n//   [ u8 field name length ]\n//   [ UTF8 encoded field name ]\n//   [ u8 precision ]\n// ```\nfunction packFieldConfig(fields: NormalizedFieldConfig) {\n  const out = new ArrayBuffer(1024);\n  const outView = new DataView(out);\n  let pos = 0;\n\n  outView.setUint8(pos, PACKED_VERSION);\n  pos += 1;\n\n  const encoder = new TextEncoder();\n  for (const fieldConfig of fields) {\n    const name = encoder.encode(fieldConfig.name);\n\n    outView.setUint8(pos, name.length);\n    pos += 1;\n\n    new Uint8Array(out, pos, name.length).set(name);\n    pos += name.length;\n\n    outView.setUint8(pos, fieldConfig.precision);\n    pos += 1;\n  }\n  return out.slice(0, pos);\n}\n\n// Pack a document's sample record into a binary buffer.\n//\n// We encode each field's history with a few layered forms of\n// compression:\n// 1. Quantization: Turn each floating point number into an integer\n//    by multiplying by 2^precision and then `Math.floor()`.\n// 2. Delta encoding: Assume that values are continuous and don't\n//    abruptly change over time, so their differences will be small.\n//    This step turns the large integers from (1) into small ones.\n// 3. Run length encoding (optional): Assume that some quantities\n//    in the system will have constant velocity, so encode `k`\n//    repetitions of `n` as `[k, n]`. If run length encoding doesn't\n//    make (2) smaller, we skip it.\n// 4. Varint encoding: Using FastIntegerCompression.js, we use a\n//    variable length integer encoding that uses fewer bytes for\n//    smaller numbers.\n//\n// Format:\n// ```\n// [ 4 byte xxhash of packed field config ]\n//\n// for each set field:\n//   [ 0 0 0 useRLE? ]\n//   [ u4 field number ]\n//\n//   Sample timestamps:\n//   [ u64le initial timestamp ]\n//   [ u16le timestamp buffer length ]\n//   [ vint(RLE(delta(remaining timestamps)))]\n//\n//   Sample values:\n//   [ u16le value buffer length ]\n//   [ vint(RLE?(delta([initialValue, ...values])))]\n// ```\nexport function packSampleRecord(\n  fields: NormalizedFieldConfig,\n  sampleRecord: Record<string, History>,\n): ArrayBuffer {\n  const out = new ArrayBuffer(65536);\n  const outView = new DataView(out);\n  let pos = 0;\n\n  const configHash = xxHash32(new Uint8Array(packFieldConfig(fields)));\n  outView.setUint32(pos, configHash, true);\n  pos += 4;\n\n  for (let fieldNumber = 0; fieldNumber < fields.length; fieldNumber += 1) {\n    const { name, precision } = fields[fieldNumber];\n    const history = sampleRecord[name];\n    if (!history || history.samples.length === 0) {\n      continue;\n    }\n\n    const timestamps = history.samples.map((s) => Math.floor(s.time));\n    const initialTimestamp = timestamps[0];\n    const encodedTimestamps = runLengthEncode(deltaEncode(timestamps.slice(1), initialTimestamp));\n    const compressedTimestamps = compressSigned(encodedTimestamps);\n    if (compressedTimestamps.byteLength >= 1 << 16) {\n      throw new Error(`Compressed buffer too long: ${compressedTimestamps.byteLength}`);\n    }\n\n    const values = [history.initialValue, ...history.samples.map((s) => s.value)];\n    const quantized = quantize(values, precision);\n    const deltaEncoded = deltaEncode(quantized);\n    const runLengthEncoded = runLengthEncode(deltaEncoded);\n\n    // Decide if we're going to run length encode the values based on whether\n    // it actually made the encoded buffer smaller.\n    const useRLE = runLengthEncoded.length < deltaEncoded.length;\n    let fieldHeader = fieldNumber;\n    if (useRLE) {\n      fieldHeader |= 1 << 4;\n    }\n\n    const encoded = useRLE ? runLengthEncoded : deltaEncoded;\n    const compressed = compressSigned(encoded);\n    if (compressed.byteLength >= 1 << 16) {\n      throw new Error(`Compressed buffer too long: ${compressed.byteLength}`);\n    }\n\n    outView.setUint8(pos, fieldHeader);\n    pos += 1;\n\n    outView.setBigUint64(pos, BigInt(initialTimestamp), true);\n    pos += 8;\n\n    outView.setUint16(pos, compressedTimestamps.byteLength, true);\n    pos += 2;\n\n    new Uint8Array(out, pos, compressedTimestamps.byteLength).set(\n      new Uint8Array(compressedTimestamps),\n    );\n    pos += compressedTimestamps.byteLength;\n\n    outView.setUint16(pos, compressed.byteLength, true);\n    pos += 2;\n\n    new Uint8Array(out, pos, compressed.byteLength).set(new Uint8Array(compressed));\n    pos += compressed.byteLength;\n  }\n\n  return out.slice(0, pos);\n}\n\nexport function unpackSampleRecord(fields: FieldConfig, buffer: ArrayBuffer) {\n  const view = new DataView(buffer);\n  let pos = 0;\n\n  const normalizedFields = normalizeFieldConfig(fields);\n  const expectedConfigHash = xxHash32(new Uint8Array(packFieldConfig(normalizedFields)));\n\n  const configHash = view.getUint32(pos, true);\n  pos += 4;\n\n  if (configHash !== expectedConfigHash) {\n    throw new Error(`Config hash mismatch: ${configHash} !== ${expectedConfigHash}`);\n  }\n\n  const out = {} as Record<string, History>;\n  while (pos < buffer.byteLength) {\n    const fieldHeader = view.getUint8(pos);\n    pos += 1;\n\n    const fieldNumber = fieldHeader & 0b00001111;\n    const useRLE = (fieldHeader & (1 << 4)) !== 0;\n    const fieldConfig = normalizedFields[fieldNumber];\n    if (!fieldConfig) {\n      throw new Error(`Invalid field number: ${fieldNumber}`);\n    }\n\n    const initialTimestamp = Number(view.getBigUint64(pos, true));\n    pos += 8;\n\n    const compressedTimestampLength = view.getUint16(pos, true);\n    pos += 2;\n\n    const compressedTimestampBuffer = buffer.slice(pos, pos + compressedTimestampLength);\n    pos += compressedTimestampLength;\n\n    const timestamps = [\n      initialTimestamp,\n      ...deltaDecode(\n        runLengthDecode(uncompressSigned(compressedTimestampBuffer)),\n        initialTimestamp,\n      ),\n    ];\n\n    const compressedLength = view.getUint16(pos, true);\n    pos += 2;\n\n    const compressedBuffer = buffer.slice(pos, pos + compressedLength);\n    pos += compressedLength;\n\n    const encoded = uncompressSigned(compressedBuffer);\n    const deltaEncoded = useRLE ? runLengthDecode(encoded) : encoded;\n    const quantized = deltaDecode(deltaEncoded);\n    const values = unquantize(quantized, fieldConfig.precision);\n\n    if (timestamps.length + 1 !== values.length) {\n      throw new Error(`Invalid sample record: ${timestamps.length} + 1 !== ${values.length}`);\n    }\n    const initialValue = values[0];\n    const samples = [];\n    for (let i = 0; i < timestamps.length; i++) {\n      const time = timestamps[i];\n      const value = values[i + 1];\n      samples.push({ value, time });\n    }\n    const history = { initialValue, samples };\n    out[fieldConfig.name] = history;\n  }\n  return out;\n}\n\nfunction normalizeFieldConfig(fields: FieldConfig): NormalizedFieldConfig {\n  return fields.map((f) => (typeof f === 'string' ? { name: f, precision: 0 } : f));\n}\n"
  },
  {
    "path": "convex/engine/schema.ts",
    "content": "import { defineTable } from 'convex/server';\nimport { Infer, v } from 'convex/values';\n\nconst input = v.object({\n  // Inputs are scoped to a single engine.\n  engineId: v.id('engines'),\n\n  // Monotonically increasing input number within a world starting at 0.\n  number: v.number(),\n\n  // Name of the input handler to run.\n  name: v.string(),\n  // Dynamically typed arguments and return value for the input handler. We'll\n  // provide type safety at a higher layer.\n  args: v.any(),\n  returnValue: v.optional(\n    v.union(\n      v.object({\n        kind: v.literal('ok'),\n        value: v.any(),\n      }),\n      v.object({\n        kind: v.literal('error'),\n        message: v.string(),\n      }),\n    ),\n  ),\n\n  // Timestamp when the server received the input. This timestamp is best-effort,\n  // since we don't guarantee strict monotonicity here. So, an input may not get\n  // assigned to the engine step whose time interval contains this timestamp.\n  received: v.number(),\n});\n\nexport const engine = v.object({\n  // What is the current simulation time for the engine? Monotonically increasing.\n  currentTime: v.optional(v.number()),\n  // What was `currentTime` for the preceding step of the engine?\n  lastStepTs: v.optional(v.number()),\n\n  // How far has the engine processed in the input queue?\n  processedInputNumber: v.optional(v.number()),\n\n  running: v.boolean(),\n\n  // Monotonically increasing counter that serializes all engine runs. If we ever\n  // end up with two steps overlapping in time, this counter will force them to\n  // conflict.\n  generationNumber: v.number(),\n});\nexport type Engine = Infer<typeof engine>;\n\nexport const engineTables = {\n  inputs: defineTable(input).index('byInputNumber', ['engineId', 'number']),\n  engines: defineTable(engine),\n};\n"
  },
  {
    "path": "convex/http.ts",
    "content": "import { httpRouter } from 'convex/server';\nimport { handleReplicateWebhook } from './music';\n\nconst http = httpRouter();\nhttp.route({\n  path: '/replicate_webhook',\n  method: 'POST',\n  handler: handleReplicateWebhook,\n});\nexport default http;\n"
  },
  {
    "path": "convex/init.ts",
    "content": "import { v } from 'convex/values';\nimport { internal } from './_generated/api';\nimport { DatabaseReader, MutationCtx, mutation } from './_generated/server';\nimport { Descriptions } from '../data/characters';\nimport * as map from '../data/gentle';\nimport { insertInput } from './aiTown/insertInput';\nimport { Id } from './_generated/dataModel';\nimport { createEngine } from './aiTown/main';\nimport { ENGINE_ACTION_DURATION } from './constants';\nimport { detectMismatchedLLMProvider } from './util/llm';\n\nconst init = mutation({\n  args: {\n    numAgents: v.optional(v.number()),\n  },\n  handler: async (ctx, args) => {\n    detectMismatchedLLMProvider();\n    const { worldStatus, engine } = await getOrCreateDefaultWorld(ctx);\n    if (worldStatus.status !== 'running') {\n      console.warn(\n        `Engine ${engine._id} is not active! Run \"npx convex run testing:resume\" to restart it.`,\n      );\n      return;\n    }\n    const shouldCreate = await shouldCreateAgents(\n      ctx.db,\n      worldStatus.worldId,\n      worldStatus.engineId,\n    );\n    if (shouldCreate) {\n      const toCreate = args.numAgents !== undefined ? args.numAgents : Descriptions.length;\n      for (let i = 0; i < toCreate; i++) {\n        await insertInput(ctx, worldStatus.worldId, 'createAgent', {\n          descriptionIndex: i % Descriptions.length,\n        });\n      }\n    }\n  },\n});\nexport default init;\n\nasync function getOrCreateDefaultWorld(ctx: MutationCtx) {\n  const now = Date.now();\n\n  let worldStatus = await ctx.db\n    .query('worldStatus')\n    .filter((q) => q.eq(q.field('isDefault'), true))\n    .unique();\n  if (worldStatus) {\n    const engine = (await ctx.db.get(worldStatus.engineId))!;\n    return { worldStatus, engine };\n  }\n\n  const engineId = await createEngine(ctx);\n  const engine = (await ctx.db.get(engineId))!;\n  const worldId = await ctx.db.insert('worlds', {\n    nextId: 0,\n    agents: [],\n    conversations: [],\n    players: [],\n  });\n  const worldStatusId = await ctx.db.insert('worldStatus', {\n    engineId: engineId,\n    isDefault: true,\n    lastViewed: now,\n    status: 'running',\n    worldId: worldId,\n  });\n  worldStatus = (await ctx.db.get(worldStatusId))!;\n  await ctx.db.insert('maps', {\n    worldId,\n    width: map.mapwidth,\n    height: map.mapheight,\n    tileSetUrl: map.tilesetpath,\n    tileSetDimX: map.tilesetpxw,\n    tileSetDimY: map.tilesetpxh,\n    tileDim: map.tiledim,\n    bgTiles: map.bgtiles,\n    objectTiles: map.objmap,\n    animatedSprites: map.animatedsprites,\n  });\n  await ctx.scheduler.runAfter(0, internal.aiTown.main.runStep, {\n    worldId,\n    generationNumber: engine.generationNumber,\n    maxDuration: ENGINE_ACTION_DURATION,\n  });\n  return { worldStatus, engine };\n}\n\nasync function shouldCreateAgents(\n  db: DatabaseReader,\n  worldId: Id<'worlds'>,\n  engineId: Id<'engines'>,\n) {\n  const world = await db.get(worldId);\n  if (!world) {\n    throw new Error(`Invalid world ID: ${worldId}`);\n  }\n  if (world.agents.length > 0) {\n    return false;\n  }\n  const unactionedJoinInputs = await db\n    .query('inputs')\n    .withIndex('byInputNumber', (q) => q.eq('engineId', engineId))\n    .order('asc')\n    .filter((q) => q.eq(q.field('name'), 'createAgent'))\n    .filter((q) => q.eq(q.field('returnValue'), undefined))\n    .first();\n  if (unactionedJoinInputs) {\n    return false;\n  }\n  return true;\n}\n"
  },
  {
    "path": "convex/messages.ts",
    "content": "import { v } from 'convex/values';\nimport { mutation, query } from './_generated/server';\nimport { insertInput } from './aiTown/insertInput';\nimport { conversationId, playerId } from './aiTown/ids';\n\nexport const listMessages = query({\n  args: {\n    worldId: v.id('worlds'),\n    conversationId,\n  },\n  handler: async (ctx, args) => {\n    const messages = await ctx.db\n      .query('messages')\n      .withIndex('conversationId', (q) => q.eq('worldId', args.worldId).eq('conversationId', args.conversationId))\n      .collect();\n    const out = [];\n    for (const message of messages) {\n      const playerDescription = await ctx.db\n        .query('playerDescriptions')\n        .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', message.author))\n        .first();\n      if (!playerDescription) {\n        throw new Error(`Invalid author ID: ${message.author}`);\n      }\n      out.push({ ...message, authorName: playerDescription.name });\n    }\n    return out;\n  },\n});\n\nexport const writeMessage = mutation({\n  args: {\n    worldId: v.id('worlds'),\n    conversationId,\n    messageUuid: v.string(),\n    playerId,\n    text: v.string(),\n  },\n  handler: async (ctx, args) => {\n    await ctx.db.insert('messages', {\n      conversationId: args.conversationId,\n      author: args.playerId,\n      messageUuid: args.messageUuid,\n      text: args.text,\n      worldId: args.worldId,\n    });\n    await insertInput(ctx, args.worldId, 'finishSendingMessage', {\n      conversationId: args.conversationId,\n      playerId: args.playerId,\n      timestamp: Date.now(),\n    });\n  },\n});\n"
  },
  {
    "path": "convex/music.ts",
    "content": "import { v } from 'convex/values';\nimport { query, internalMutation } from './_generated/server';\nimport Replicate, { WebhookEventType } from 'replicate';\nimport { httpAction, internalAction } from './_generated/server';\nimport { internal, api } from './_generated/api';\n\nfunction client(): Replicate {\n  const replicate = new Replicate({\n    auth: process.env.REPLICATE_API_TOKEN || '',\n  });\n  return replicate;\n}\n\nfunction replicateAvailable(): boolean {\n  return !!process.env.REPLICATE_API_TOKEN;\n}\n\nexport const insertMusic = internalMutation({\n  args: { storageId: v.string(), type: v.union(v.literal('background'), v.literal('player')) },\n  handler: async (ctx, args) => {\n    await ctx.db.insert('music', {\n      storageId: args.storageId,\n      type: args.type,\n    });\n  },\n});\n\nexport const getBackgroundMusic = query({\n  handler: async (ctx) => {\n    const music = await ctx.db\n      .query('music')\n      .filter((entry) => entry.eq(entry.field('type'), 'background'))\n      .order('desc')\n      .first();\n    if (!music) {\n      return '/ai-town/assets/background.mp3';\n    }\n    const url = await ctx.storage.getUrl(music.storageId);\n    if (!url) {\n      throw new Error(`Invalid storage ID: ${music.storageId}`);\n    }\n    return url;\n  },\n});\n\nexport const enqueueBackgroundMusicGeneration = internalAction({\n  handler: async (ctx): Promise<void> => {\n    if (!replicateAvailable()) {\n      return;\n    }\n    const worldStatus = await ctx.runQuery(api.world.defaultWorldStatus);\n    if (!worldStatus) {\n      console.log('No active default world, returning.');\n      return;\n    }\n    // TODO: MusicGen-Large on Replicate only allows 30 seconds. Use MusicGen-Small for longer?\n    await generateMusic('16-bit RPG adventure game with wholesome vibe', 30);\n  },\n});\n\nexport const handleReplicateWebhook = httpAction(async (ctx, request) => {\n  const req = await request.json();\n  if (req.id) {\n    const prediction = await client().predictions.get(req.id);\n    const response = await fetch(prediction.output);\n    const music = await response.blob();\n    const storageId = await ctx.storage.store(music);\n    await ctx.runMutation(internal.music.insertMusic, { type: 'background', storageId });\n  }\n  return new Response();\n});\n\nenum MusicGenNormStrategy {\n  Clip = 'clip',\n  Loudness = 'loudness',\n  Peak = 'peak',\n  Rms = 'rms',\n}\n\nenum MusicGenFormat {\n  wav = 'wav',\n  mp3 = 'mp3',\n}\n\n/**\n *\n * @param prompt A description of the music you want to generate.\n * @param duration Duration of the generated audio in seconds.\n * @param webhook webhook URL for Replicate to call when @param webhook_events_filter is triggered\n * @param webhook_events_filter Array of event names to filter the webhook. See https://replicate.com/docs/reference/http#predictions.create--webhook_events_filter\n * @param normalization_strategy Strategy for normalizing audio.\n * @param top_k Reduces sampling to the k most likely tokens.\n * @param top_p Reduces sampling to tokens with cumulative probability of p. When set to `0` (default), top_k sampling is used.\n * @param temperature Controls the 'conservativeness' of the sampling process. Higher temperature means more diversity.\n * @param classifer_free_gudance Increases the influence of inputs on the output. Higher values produce lower-varience outputs that adhere more closely to inputs.\n * @param output_format Output format for generated audio. See @\n * @param seed Seed for random number generator. If None or -1, a random seed will be used.\n * @returns object containing metadata of the prediction with ID to fetch once result is completed\n */\nexport async function generateMusic(\n  prompt: string,\n  duration: number,\n  webhook: string = process.env.CONVEX_SITE_URL + '/replicate_webhook' || '',\n  webhook_events_filter: [WebhookEventType] = ['completed'],\n  normalization_strategy: MusicGenNormStrategy = MusicGenNormStrategy.Peak,\n  output_format: MusicGenFormat = MusicGenFormat.mp3,\n  top_k = 250,\n  top_p = 0,\n  temperature = 1,\n  classifer_free_gudance = 3,\n  seed = -1,\n  model_version = 'large',\n) {\n  if (!replicateAvailable()) {\n    throw new Error('Replicate API token not set');\n  }\n  return await client().predictions.create({\n    // https://replicate.com/facebookresearch/musicgen/versions/7a76a8258b23fae65c5a22debb8841d1d7e816b75c2f24218cd2bd8573787906\n    version: '7a76a8258b23fae65c5a22debb8841d1d7e816b75c2f24218cd2bd8573787906',\n    input: {\n      model_version,\n      prompt,\n      duration,\n      normalization_strategy,\n      top_k,\n      top_p,\n      temperature,\n      classifer_free_gudance,\n      output_format,\n      seed,\n    },\n    webhook,\n    webhook_events_filter,\n  });\n}\n"
  },
  {
    "path": "convex/schema.ts",
    "content": "import { defineSchema, defineTable } from 'convex/server';\nimport { v } from 'convex/values';\nimport { agentTables } from './agent/schema';\nimport { aiTownTables } from './aiTown/schema';\nimport { conversationId, playerId } from './aiTown/ids';\nimport { engineTables } from './engine/schema';\n\nexport default defineSchema({\n  music: defineTable({\n    storageId: v.string(),\n    type: v.union(v.literal('background'), v.literal('player')),\n  }),\n\n  messages: defineTable({\n    conversationId,\n    messageUuid: v.string(),\n    author: playerId,\n    text: v.string(),\n    worldId: v.optional(v.id('worlds')),\n  })\n    .index('conversationId', ['worldId', 'conversationId'])\n    .index('messageUuid', ['conversationId', 'messageUuid']),\n\n  ...agentTables,\n  ...aiTownTables,\n  ...engineTables,\n});\n"
  },
  {
    "path": "convex/testing.ts",
    "content": "import { Id, TableNames } from './_generated/dataModel';\nimport { internal } from './_generated/api';\nimport {\n  DatabaseReader,\n  internalAction,\n  internalMutation,\n  mutation,\n  query,\n} from './_generated/server';\nimport { v } from 'convex/values';\nimport schema from './schema';\nimport { DELETE_BATCH_SIZE } from './constants';\nimport { kickEngine, startEngine, stopEngine } from './aiTown/main';\nimport { insertInput } from './aiTown/insertInput';\nimport { fetchEmbedding } from './util/llm';\nimport { chatCompletion } from './util/llm';\nimport { startConversationMessage } from './agent/conversation';\nimport { GameId } from './aiTown/ids';\n\n// Clear all of the tables except for the embeddings cache.\nconst excludedTables: Array<TableNames> = ['embeddingsCache'];\n\nexport const wipeAllTables = internalMutation({\n  handler: async (ctx) => {\n    for (const tableName of Object.keys(schema.tables)) {\n      if (excludedTables.includes(tableName as TableNames)) {\n        continue;\n      }\n      await ctx.scheduler.runAfter(0, internal.testing.deletePage, { tableName, cursor: null });\n    }\n  },\n});\n\nexport const deletePage = internalMutation({\n  args: {\n    tableName: v.string(),\n    cursor: v.union(v.string(), v.null()),\n  },\n  handler: async (ctx, args) => {\n    const results = await ctx.db\n      .query(args.tableName as TableNames)\n      .paginate({ cursor: args.cursor, numItems: DELETE_BATCH_SIZE });\n    for (const row of results.page) {\n      await ctx.db.delete(row._id);\n    }\n    if (!results.isDone) {\n      await ctx.scheduler.runAfter(0, internal.testing.deletePage, {\n        tableName: args.tableName,\n        cursor: results.continueCursor,\n      });\n    }\n  },\n});\n\nexport const kick = internalMutation({\n  handler: async (ctx) => {\n    const { worldStatus } = await getDefaultWorld(ctx.db);\n    await kickEngine(ctx, worldStatus.worldId);\n  },\n});\n\nexport const stopAllowed = query({\n  handler: async () => {\n    return !process.env.STOP_NOT_ALLOWED;\n  },\n});\n\nexport const stop = mutation({\n  handler: async (ctx) => {\n    if (process.env.STOP_NOT_ALLOWED) throw new Error('Stop not allowed');\n    const { worldStatus, engine } = await getDefaultWorld(ctx.db);\n    if (worldStatus.status === 'inactive' || worldStatus.status === 'stoppedByDeveloper') {\n      if (engine.running) {\n        throw new Error(`Engine ${engine._id} isn't stopped?`);\n      }\n      console.debug(`World ${worldStatus.worldId} is already inactive`);\n      return;\n    }\n    console.log(`Stopping engine ${engine._id}...`);\n    await ctx.db.patch(worldStatus._id, { status: 'stoppedByDeveloper' });\n    await stopEngine(ctx, worldStatus.worldId);\n  },\n});\n\nexport const resume = mutation({\n  handler: async (ctx) => {\n    const { worldStatus, engine } = await getDefaultWorld(ctx.db);\n    if (worldStatus.status === 'running') {\n      if (!engine.running) {\n        throw new Error(`Engine ${engine._id} isn't running?`);\n      }\n      console.debug(`World ${worldStatus.worldId} is already running`);\n      return;\n    }\n    console.log(\n      `Resuming engine ${engine._id} for world ${worldStatus.worldId} (state: ${worldStatus.status})...`,\n    );\n    await ctx.db.patch(worldStatus._id, { status: 'running' });\n    await startEngine(ctx, worldStatus.worldId);\n  },\n});\n\nexport const archive = internalMutation({\n  handler: async (ctx) => {\n    const { worldStatus, engine } = await getDefaultWorld(ctx.db);\n    if (engine.running) {\n      throw new Error(`Engine ${engine._id} is still running!`);\n    }\n    console.log(`Archiving world ${worldStatus.worldId}...`);\n    await ctx.db.patch(worldStatus._id, { isDefault: false });\n  },\n});\n\nasync function getDefaultWorld(db: DatabaseReader) {\n  const worldStatus = await db\n    .query('worldStatus')\n    .filter((q) => q.eq(q.field('isDefault'), true))\n    .first();\n  if (!worldStatus) {\n    throw new Error('No default world found');\n  }\n  const engine = await db.get(worldStatus.engineId);\n  if (!engine) {\n    throw new Error(`Engine ${worldStatus.engineId} not found`);\n  }\n  return { worldStatus, engine };\n}\n\nexport const debugCreatePlayers = internalMutation({\n  args: {\n    numPlayers: v.number(),\n  },\n  handler: async (ctx, args) => {\n    const { worldStatus } = await getDefaultWorld(ctx.db);\n    for (let i = 0; i < args.numPlayers; i++) {\n      const inputId = await insertInput(ctx, worldStatus.worldId, 'join', {\n        name: `Robot${i}`,\n        description: `This player is a robot.`,\n        character: `f${1 + (i % 8)}`,\n      });\n    }\n  },\n});\n\nexport const randomPositions = internalMutation({\n  handler: async (ctx) => {\n    const { worldStatus } = await getDefaultWorld(ctx.db);\n    const map = await ctx.db\n      .query('maps')\n      .withIndex('worldId', (q) => q.eq('worldId', worldStatus.worldId))\n      .unique();\n    if (!map) {\n      throw new Error(`No map for world ${worldStatus.worldId}`);\n    }\n    const world = await ctx.db.get(worldStatus.worldId);\n    if (!world) {\n      throw new Error(`No world for world ${worldStatus.worldId}`);\n    }\n    for (const player of world.players) {\n      await insertInput(ctx, world._id, 'moveTo', {\n        playerId: player.id,\n        destination: {\n          x: 1 + Math.floor(Math.random() * (map.width - 2)),\n          y: 1 + Math.floor(Math.random() * (map.height - 2)),\n        },\n      });\n    }\n  },\n});\n\nexport const testEmbedding = internalAction({\n  args: { input: v.string() },\n  handler: async (_ctx, args) => {\n    return await fetchEmbedding(args.input);\n  },\n});\n\nexport const testCompletion = internalAction({\n  args: {},\n  handler: async (ctx, args) => {\n    return await chatCompletion({\n      messages: [\n        { content: 'You are helpful', role: 'system' },\n        { content: 'Where is pizza?', role: 'user' },\n      ],\n    });\n  },\n});\n\nexport const testConvo = internalAction({\n  args: {},\n  handler: async (ctx, args) => {\n    const a: any = (await startConversationMessage(\n      ctx,\n      'm1707m46wmefpejw1k50rqz7856qw3ew' as Id<'worlds'>,\n      'c:115' as GameId<'conversations'>,\n      'p:0' as GameId<'players'>,\n      'p:6' as GameId<'players'>,\n    )) as any;\n    return await a.readAll();\n  },\n});\n"
  },
  {
    "path": "convex/util/FastIntegerCompression.ts",
    "content": "/**\n * FastIntegerCompression.js : a fast integer compression library in JavaScript.\n * From https://github.com/lemire/FastIntegerCompression.js/\n * (c) the authors\n * Licensed under the Apache License, Version 2.0.\n *\n *FastIntegerCompression\n * Simple usage :\n *  // var FastIntegerCompression = require(\"fastintcompression\");// if you use node\n *  var array = [10,100000,65999,10,10,0,1,1,2000];\n *  var buf = FastIntegerCompression.compress(array);\n *  var back = FastIntegerCompression.uncompress(buf); // gets back [10,100000,65999,10,10,0,1,1,2000]\n *\n *\n * You can install the library under node with the command line\n *   npm install fastintcompression\n */\n\nfunction bytelog(val: number) {\n  if (val < 1 << 7) {\n    return 1;\n  } else if (val < 1 << 14) {\n    return 2;\n  } else if (val < 1 << 21) {\n    return 3;\n  } else if (val < 1 << 28) {\n    return 4;\n  }\n  return 5;\n}\n\nfunction zigzag_encode(val: number) {\n  return (val + val) ^ (val >> 31);\n}\n\nfunction zigzag_decode(val: number) {\n  return (val >> 1) ^ -(val & 1);\n}\n\n// Compute how many bytes an array of integers would use once compressed.\n// The input is expected to be an array of non-negative integers.\nexport function computeCompressedSizeInBytes(input: number[]) {\n  var c = input.length;\n  var answer = 0;\n  for (var i = 0; i < c; i++) {\n    answer += bytelog(input[i]);\n  }\n  return answer;\n}\n\n// Compute how many bytes an array of integers would use once compressed.\n// The input is expected to be an array of integers, some of them can be negative.\nexport function computeCompressedSizeInBytesSigned(input: number[]) {\n  var c = input.length;\n  var answer = 0;\n  for (var i = 0; i < c; i++) {\n    answer += bytelog(zigzag_encode(input[i]));\n  }\n  return answer;\n}\n\n// Compress an array of integers, return a compressed buffer (as an ArrayBuffer).\n// It is expected that the integers are non-negative: the caller is responsible\n// for making this check. Floating-point numbers are not supported.\nexport function compress(input: number[]) {\n  var c = input.length;\n  var buf = new ArrayBuffer(computeCompressedSizeInBytes(input));\n  var view = new Int8Array(buf);\n  var pos = 0;\n  for (var i = 0; i < c; i++) {\n    var val = input[i];\n    if (val < 1 << 7) {\n      view[pos++] = val;\n    } else if (val < 1 << 14) {\n      view[pos++] = (val & 0x7f) | 0x80;\n      view[pos++] = val >>> 7;\n    } else if (val < 1 << 21) {\n      view[pos++] = (val & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 7) & 0x7f) | 0x80;\n      view[pos++] = val >>> 14;\n    } else if (val < 1 << 28) {\n      view[pos++] = (val & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 7) & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 14) & 0x7f) | 0x80;\n      view[pos++] = val >>> 21;\n    } else {\n      view[pos++] = (val & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 7) & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 14) & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 21) & 0x7f) | 0x80;\n      view[pos++] = val >>> 28;\n    }\n  }\n  return buf;\n}\n\n// From a compressed array of integers stored ArrayBuffer,\n// compute the number of compressed integers by scanning the input.\nexport function computeHowManyIntegers(input: ArrayBuffer) {\n  var view = new Uint8Array(input);\n  var c = view.length;\n  var count = 0;\n  for (var i = 0; i < c; i++) {\n    count += view[i] >>> 7;\n  }\n  return c - count;\n}\n// Uncompress an array of integer from an ArrayBuffer, return the array.\n// It is assumed that they were compressed using the compress function, the caller\n// is responsible for ensuring that it is the case.\nexport function uncompress(input: ArrayBuffer) {\n  var array = []; // The size of the output is not yet known.\n  var inbyte = new Int8Array(input);\n  var end = inbyte.length;\n  var pos = 0;\n  while (end > pos) {\n    var c = inbyte[pos++];\n    var v = c & 0x7f;\n    if (c >= 0) {\n      array.push(v);\n      continue;\n    }\n    c = inbyte[pos++];\n    v |= (c & 0x7f) << 7;\n    if (c >= 0) {\n      array.push(v);\n      continue;\n    }\n    c = inbyte[pos++];\n    v |= (c & 0x7f) << 14;\n    if (c >= 0) {\n      array.push(v);\n      continue;\n    }\n    c = inbyte[pos++];\n    v |= (c & 0x7f) << 21;\n    if (c >= 0) {\n      array.push(v);\n      continue;\n    }\n    c = inbyte[pos++];\n    v |= c << 28;\n    v >>>= 0; // make positive\n    array.push(v);\n  }\n  return array;\n}\n\n// Compress an array of integers, return a compressed buffer (as an ArrayBuffer).\n// The integers can be signed (negative), but floating-point values are not supported.\nexport function compressSigned(input: number[]) {\n  var c = input.length;\n  var buf = new ArrayBuffer(computeCompressedSizeInBytesSigned(input));\n  var view = new Int8Array(buf);\n  var pos = 0;\n  for (var i = 0; i < c; i++) {\n    var val = zigzag_encode(input[i]);\n    if (val < 1 << 7) {\n      view[pos++] = val;\n    } else if (val < 1 << 14) {\n      view[pos++] = (val & 0x7f) | 0x80;\n      view[pos++] = val >>> 7;\n    } else if (val < 1 << 21) {\n      view[pos++] = (val & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 7) & 0x7f) | 0x80;\n      view[pos++] = val >>> 14;\n    } else if (val < 1 << 28) {\n      view[pos++] = (val & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 7) & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 14) & 0x7f) | 0x80;\n      view[pos++] = val >>> 21;\n    } else {\n      view[pos++] = (val & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 7) & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 14) & 0x7f) | 0x80;\n      view[pos++] = ((val >>> 21) & 0x7f) | 0x80;\n      view[pos++] = val >>> 28;\n    }\n  }\n  return buf;\n}\n\n// Uncompress an array of integer from an ArrayBuffer, return the array.\n// It is assumed that they were compressed using the compressSigned function, the caller\n// is responsible for ensuring that it is the case.\nexport function uncompressSigned(input: ArrayBuffer) {\n  var array = []; // The size of the output is not yet known.\n  var inbyte = new Int8Array(input);\n  var end = inbyte.length;\n  var pos = 0;\n  while (end > pos) {\n    var c = inbyte[pos++];\n    var v = c & 0x7f;\n    if (c >= 0) {\n      array.push(zigzag_decode(v));\n      continue;\n    }\n    c = inbyte[pos++];\n    v |= (c & 0x7f) << 7;\n    if (c >= 0) {\n      array.push(zigzag_decode(v));\n      continue;\n    }\n    c = inbyte[pos++];\n    v |= (c & 0x7f) << 14;\n    if (c >= 0) {\n      array.push(zigzag_decode(v));\n      continue;\n    }\n    c = inbyte[pos++];\n    v |= (c & 0x7f) << 21;\n    if (c >= 0) {\n      array.push(zigzag_decode(v));\n      continue;\n    }\n    c = inbyte[pos++];\n    v |= c << 28;\n    array.push(zigzag_decode(v));\n  }\n  return array;\n}\n"
  },
  {
    "path": "convex/util/assertNever.ts",
    "content": "// From https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#union-exhaustiveness-checking\nexport function assertNever(x: never): never {\n  throw new Error(`Unexpected object: ${JSON.stringify(x)}`);\n}\n"
  },
  {
    "path": "convex/util/asyncMap.test.ts",
    "content": "import { asyncMap } from './asyncMap';\n\ndescribe('asyncMap', () => {\n  it('should map over a list asynchronously', async () => {\n    const list = [1, 2, 3];\n    const result = await asyncMap(list, async (item: number) => item * 2);\n    expect(result).toEqual([2, 4, 6]);\n  });\n\n  it('should handle empty list input', async () => {\n    const list: number[] = [];\n    const result = await asyncMap(list, async (item: number) => item * 2);\n    expect(result).toEqual([]);\n  });\n});"
  },
  {
    "path": "convex/util/asyncMap.ts",
    "content": "/**\n * asyncMap returns the results of applying an async function over an list.\n *\n * @param list - Iterable object of items, e.g. an Array, Set, Object.keys\n * @param asyncTransform\n * @returns\n */\n\nexport async function asyncMap<FromType, ToType>(\n  list: Iterable<FromType>,\n  asyncTransform: (item: FromType, index: number) => Promise<ToType>,\n): Promise<ToType[]> {\n  const promises: Promise<ToType>[] = [];\n  let idx = 0;\n  for (const item of list) {\n    promises.push(asyncTransform(item, idx));\n    idx += 1;\n  }\n  return Promise.all(promises);\n}\n"
  },
  {
    "path": "convex/util/compression.test.ts",
    "content": "import {\n  deltaDecode,\n  deltaEncode,\n  quantize,\n  runLengthDecode,\n  runLengthEncode,\n  unquantize,\n} from './compression';\n\ndescribe('compression', () => {\n  test('quantize (approximately) roundtrips', () => {\n    const precisions = [-1, 0, 1, 4, 8];\n    const datasets = [\n      // Random samples from [-2^32, 2^32]\n      [\n        -2331813745.435792, 4165391630.4586916, 2508162414.104561, -3815881222.355323,\n        3182227671.241928, -2091141304.634983, -3454731809.638463, 1539778764.4030657,\n        3723556916.971266, 4014694279.989772, 1165331218.5641785, -4209073662.9696226,\n        -3837962324.440032, 2145014827.7712336, -631662265.4694176, 4116219084.927844,\n      ],\n\n      // [-2^16, 2^16]\n      [\n        -29109.399926296363, 24836.163035466132, 59528.43800645282, 5706.0239888604265,\n        61844.35496542655, -46030.9434605508, 10288.243500897894, -48623.38350764701,\n        -62182.09862667126, 20639.535833017246, -7691.974206406943, -44505.52704528734,\n        -28755.644095767944, 38244.45061335398, -14135.607864461621, -14792.956311113172,\n      ],\n\n      // [-2^8, 2^8]\n      [\n        -67.02672070745166, -117.41024397385388, -243.41065459675673, 160.3825635900851,\n        191.79026087008378, 89.76668679513216, -10.719096486254784, 205.25021491717217,\n        -68.83096015839055, 44.321620651742364, -203.44266714551503, -19.734642986127426,\n        159.0214530150044, 72.07459707399431, -242.49909539291787, -246.50759645751867,\n      ],\n\n      // [-2^4, 2^4]\n      [\n        14.993015665565746, -14.206729228453774, -1.503306544783097, -8.618521795982875,\n        15.14825900944064, -0.7561338814569538, -4.372631369200661, -14.296889398516797,\n        -0.7673738652041102, 5.880288329769968, -0.12246711347653516, 2.6074790469727773,\n        -1.0378494460674226, -5.395209965702431, -0.9218194118035932, -1.8677599340100492,\n      ],\n    ];\n    for (const values of datasets) {\n      for (const precision of precisions) {\n        const maxError = Math.max(1 / (1 << precision), 1e-8);\n        const roundTripped = unquantize(quantize(values, precision), precision);\n        expect(values.length).toEqual(roundTripped.length);\n        for (let i = 0; i < values.length; i++) {\n          const value = values[i];\n          const roundtrippedValue = roundTripped[i];\n          expect(Math.abs(value - roundtrippedValue)).toBeLessThanOrEqual(maxError);\n        }\n      }\n    }\n  });\n\n  test('delta encode roundtrips', () => {\n    const data = [\n      41476, -13450, -59451, -65102, -32493, -39078, -53884, 40784, 32081, -40422, 43421, 17184,\n      23042, 27548, -61705, -45215, -39037, 61611, -43945, 28001, -64417, -54192, -56325, 24401,\n      17735, 37464, -39842, 54964, 14469, -47248, -39450,\n    ];\n    const roundtripped = deltaDecode(deltaEncode(data));\n    expect(data).toEqual(roundtripped);\n  });\n\n  test('run length encode roundtrips', () => {\n    const datasets = [\n      // No repetitions.\n      [\n        41476, -13450, -59451, -65102, -32493, -39078, -53884, 40784, 32081, -40422, 43421, 17184,\n        23042, 27548, -61705, -45215, -39037, 61611, -43945, 28001, -64417, -54192, -56325, 24401,\n        17735, 37464, -39842, 54964, 14469, -47248, -39450,\n      ],\n      // All repetitions.\n      [10, 10, 10, 10, 10, 10],\n      // Just one value.\n      [11],\n      // Repetitions in the middle of unique values.\n      [1, 2, 3, 4, 4, 4, 4, 5, 6, 7],\n    ];\n    for (const data of datasets) {\n      const roundtripped = runLengthDecode(runLengthEncode(data));\n      expect(data).toEqual(roundtripped);\n    }\n  });\n});\n"
  },
  {
    "path": "convex/util/compression.ts",
    "content": "export function quantize(values: number[], precision: number) {\n  const factor = 1 << precision;\n  return values.map((v) => Math.floor(v * factor));\n}\n\nexport function unquantize(quantized: number[], precision: number) {\n  const reciprocal = 1 / (1 << precision);\n  return quantized.map((q) => q * reciprocal);\n}\n\nexport function deltaEncode(values: number[], initialValue = 0) {\n  let prev = initialValue;\n  const deltas = [];\n  for (const value of values) {\n    deltas.push(value - prev);\n    prev = value;\n  }\n  return deltas;\n}\n\nexport function deltaDecode(deltas: number[], initialValue = 0) {\n  let prev = initialValue;\n  const values = [];\n  for (const delta of deltas) {\n    const value = prev + delta;\n    values.push(value);\n    prev = value;\n  }\n  return values;\n}\n\nexport function runLengthEncode(values: number[]) {\n  let hasPrevious = false;\n  let previous = 0;\n  let count = 0;\n  const encoded = [];\n  for (const value of values) {\n    if (!hasPrevious) {\n      previous = value;\n      count = 1;\n      hasPrevious = true;\n      continue;\n    }\n    if (previous === value) {\n      count += 1;\n      continue;\n    }\n    encoded.push(previous, count);\n    previous = value;\n    count = 1;\n  }\n  if (hasPrevious) {\n    encoded.push(previous, count);\n  }\n  return encoded;\n}\n\nexport function runLengthDecode(encoded: number[]) {\n  if (encoded.length % 2 !== 0) {\n    throw new Error(`Invalid RLE encoded length: ${encoded.length}`);\n  }\n  const values = [];\n  for (let i = 0; i < encoded.length; i += 2) {\n    const value = encoded[i];\n    const count = encoded[i + 1];\n    for (let j = 0; j < count; j++) {\n      values.push(value);\n    }\n  }\n  return values;\n}\n"
  },
  {
    "path": "convex/util/geometry.test.ts",
    "content": "import { compressPath, distance, manhattanDistance, normalize, orientationDegrees, pathOverlaps, pathPosition, pointsEqual, vector, vectorLength } from './geometry';\nimport { Path, Vector } from './types';\n\ndescribe('distance', () => {\n  test('should return the correct distance for two points', () => {\n    const p0 = { x: 0, y: 0 };\n    const p1 = { x: 3, y: 4 };\n    const expectedDistance = 5;\n\n    const actualDistance = distance(p0, p1);\n\n    expect(actualDistance).toBe(expectedDistance);\n  });\n\n  test('should return 0 for the same point', () => {\n    const p0 = { x: 1, y: 2 };\n    const expectedDistance = 0;\n\n    const actualDistance = distance(p0, p0);\n\n    expect(actualDistance).toBe(expectedDistance);\n  });\n\n  test('should return the correct distance for negative points', () => {\n    const p0 = { x: -2, y: -3 };\n    const p1 = { x: 1, y: 2 };\n    const expectedDistance = 5.83;\n\n    const actualDistance = distance(p0, p1);\n\n    expect(actualDistance).toBeCloseTo(expectedDistance);\n  });\n});\n\ndescribe('pointsEqual', () => {\n  test('should return true for identical points', () => {\n    const p0 = { x: 1, y: 2 };\n    const p1 = { x: 1, y: 2 };\n    expect(pointsEqual(p0, p1)).toBe(true);\n  });\n\n  test('should return false for non-idential points', () => {\n    const p0 = { x: 3, y: 2 };\n    const p1 = { x: 5, y: 3 };\n    expect(pointsEqual(p0, p1)).toBe(false);\n  });\n\n  test('should return false for different x coordinates', () => {\n    const p0 = { x: 1, y: 2 };\n    const p1 = { x: 2, y: 2 };\n    expect(pointsEqual(p0, p1)).toBe(false);\n  });\n\n  test('should return false for different y coordinates', () => {\n    const p0 = { x: 1, y: 2 };\n    const p1 = { x: 1, y: 3 };\n    expect(pointsEqual(p0, p1)).toBe(false);\n  });\n});\n\ndescribe(\"manhattanDistance\", () => {\n  test(\"should return correct distance for points on the same axis\", () => {\n    const p0 = { x: 1, y: 0 };\n    const p1 = { x: 1, y: 2 };\n    expect(manhattanDistance(p0, p1)).toBe(2);\n  });\n\n  test(\"should return correct distance for points on different axes\", () => {\n    const p0 = { x: 1, y: 0 };\n    const p1 = { x: 3, y: 2 };\n    expect(manhattanDistance(p0, p1)).toBe(4);\n  });\n\n  test(\"should return correct distance for negative points\", () => {\n    const p0 = { x: -2, y: 0 };\n    const p1 = { x: 1, y: -2 };\n    expect(manhattanDistance(p0, p1)).toBe(5);\n  });\n\n  test(\"should return correct distance for identical points\", () => {\n    const p0 = { x: 1, y: 2 };\n    const p1 = { x: 1, y: 2 };\n    expect(manhattanDistance(p0, p1)).toBe(0);\n  });\n});\n\ndescribe('pathOverlaps', () => {\n  test('should throw an error if the path does not have 2 entries', () => {\n    const path: Path = [\n      [0, 0, 0, 1, 0]\n    ];\n    const time = 0;\n    expect(() => pathOverlaps(path, time)).toThrowError('Invalid path: [[0,0,0,1,0]]');\n  });\n\n  test('should return true if the time is within the path', () => {\n    const path: Path = [\n      [0, 0, 0, 1, 1],\n      [0, 2, 0, 1, 2]\n    ];\n    const time = 1.5;\n    expect(pathOverlaps(path, time)).toBe(true);\n  });\n\n  test('should return false if the time is before the start of the path', () => {\n    const path: Path = [\n      [0, 0, 0, 1, 1],\n      [0, 2, 0, 1, 2]\n    ];\n    const time = 0.5;\n    expect(pathOverlaps(path, time)).toBe(false);\n  });\n\n  test('should return false if the time is after the end of the path', () => {\n    const path: Path = [\n      [0, 0, 0, 1, 1],\n      [0, 2, 0, 1, 2]\n    ];\n    const time = 2.5;\n    expect(pathOverlaps(path, time)).toBe(false);\n  });\n});\n\ndescribe('pathPosition', () => {\n  test('should throw an error if the path does not have 2 entries', () => {\n    const path: Path = [\n      [0, 0, 0, 1, 0]\n    ];\n    const time = 0;\n    expect(() => pathPosition(path, time)).toThrowError('Invalid path: [[0,0,0,1,0]]');\n  });\n\n  test('returns the first point when time is less than the start time', () => {\n    const path: Path = [\n      [1, 2, 3, 4, 2],\n      [5, 6, 3, 4, 3]\n    ];\n\n    const result = pathPosition(path, 1);\n\n    expect(result.position).toEqual({ x: 1, y: 2 });\n    expect(result.facing).toEqual({ dx: 3, dy: 4 });\n    expect(result.velocity).toBe(0);\n  });\n\n  test('returns the last point when time is greater than the end time', () => {\n    const path: Path = [\n      [1, 2, 3, 4, 2],\n      [5, 6, 3, 4, 3]\n    ];\n\n    const result = pathPosition(path, 4);\n\n    expect(result.position).toEqual({ x: 5, y: 6 });\n    expect(result.facing).toEqual({ dx: 3, dy: 4 });\n    expect(result.velocity).toBe(0);\n  });\n\n  test('returns the interpolated point for time between two segments', () => {\n    const path: Path = [\n      [1, 2, 7, 8, 2],\n      [5, 6, 7, 8, 3],\n      [10, 11, 7, 8, 4],\n      [14, 15, 7, 8, 5]\n    ];\n\n    const result = pathPosition(path, 4.5);\n\n    expect(result.position).toEqual({ x: 12, y: 13 });\n    expect(result.facing).toEqual({ dx: 7, dy: 8 });\n    expect(result.velocity).toBeCloseTo(5.657);\n  });\n});\n\ndescribe('vector', () => {\n  test('should return a vector with dx = 1 and dy = 2', () => {\n    const p0 = { x: 1, y: 2 };\n    const p1 = { x: 2, y: 4 };\n    const expected = { dx: 1, dy: 2 };\n    const actual = vector(p0, p1);\n    expect(actual).toEqual(expected);\n  });\n\n  test('should return a vector with dx = 0 and dy = 0', () => {\n    const p0 = { x: 1, y: 2 };\n    const p1 = { x: 1, y: 2 };\n    const expected = { dx: 0, dy: 0 };\n    const actual = vector(p0, p1);\n    expect(actual).toEqual(expected);\n  });\n\n  test('should return a vector with dx = 0 and dy = -1', () => {\n    const p0 = { x: 1, y: 2 };\n    const p1 = { x: 1, y: 1 };\n    const expected = { dx: 0, dy: -1 };\n    const actual = vector(p0, p1);\n    expect(actual).toEqual(expected);\n  });\n});\n\ndescribe('vectorLength', () => {\n  test('returns the correct length for a vector', () => {\n    const vector: Vector = { dx: 3.14, dy: 4 };\n    expect(vectorLength(vector)).toBeCloseTo(5.09);\n  });\n\n  test('returns the correct length for a vector with negative components', () => {\n    const vector: Vector = { dx: -3, dy: -4 };\n    expect(vectorLength(vector)).toBeCloseTo(5);\n  });\n\n  test('returns the correct length for a vector with zero components', () => {\n    const vector: Vector = { dx: 0, dy: 0 };\n    expect(vectorLength(vector)).toBeCloseTo(0);\n  });\n});\n\ndescribe('normalize', () => {\n  test('should return null for vector length less than EPSILON', () => {\n    const vector: Vector = { dx: 0, dy: 0 };\n    const result = normalize(vector);\n    expect(result).toBeNull();\n  });\n\n  test('should return a normalized vector', () => {\n    const vector: Vector = { dx: 3, dy: 4 };\n    const result = normalize(vector);\n    expect(result).toEqual({ dx: 0.6, dy: 0.8 });\n  });\n});\n\ndescribe('orientationDegrees', () => {\n  test('should throw an error for a vector length smaller than EPSILON', () => {\n    expect(() => orientationDegrees({ dx: 0, dy: 0 })).toThrowError(\"Can't compute the orientation of too small vector {\\\"dx\\\":0,\\\"dy\\\":0}\");\n  });\n  test('should return 0 for a vector pointing to the right', () => {\n    expect(orientationDegrees({ dx: 1, dy: 0 })).toBe(0);\n  });\n\n  test('should return 90 for a vector pointing up', () => {\n    expect(orientationDegrees({ dx: 0, dy: 1 })).toBe(90);\n  });\n\n  test('should return 180 for a vector pointing to the left', () => {\n    expect(orientationDegrees({ dx: -1, dy: 0 })).toBe(180);\n  });\n\n  test('should return 270 for a vector pointing down', () => {\n    expect(orientationDegrees({ dx: 0, dy: -1 })).toBe(270);\n  });\n});\n\n\ndescribe('compressPath', () => {\n  test('should not compress a path with only 2 entries', () => {\n    const facing = { dx: 0, dy: 1 };\n    const compressed = compressPath([\n      { position: { x: 0, y: 0 }, facing, t: 0 },\n      { position: { x: 0, y: 1 }, facing, t: 1 },\n    ]);\n    expect(compressed).toEqual([\n      [0, 0, 0, 1, 0],\n      [0, 1, 0, 1, 1],\n    ]);\n  });\n\n  test('should compress a line', () => {\n    const facing = { dx: 0, dy: 1 };\n    const compressed = compressPath([\n      { position: { x: 0, y: 0 }, facing, t: 0 },\n      { position: { x: 0, y: 1 }, facing, t: 1 },\n      { position: { x: 0, y: 2 }, facing, t: 2 },\n      { position: { x: 0, y: 3 }, facing, t: 3 },\n      { position: { x: 0, y: 4 }, facing, t: 4 },\n    ]);\n    expect(compressed).toEqual([\n      [0, 0, 0, 1, 0],\n      [0, 4, 0, 1, 4],\n    ]);\n  });\n\n  test('should compress a line with a turn', () => {\n    const facingUp = { dx: 0, dy: 1 };\n    const facingRight = { dx: 1, dy: 0 };\n    const compressed = compressPath([\n      { position: { x: 0, y: 0 }, facing: facingUp, t: 0 },\n      { position: { x: 0, y: 1 }, facing: facingUp, t: 1 },\n      { position: { x: 0, y: 2 }, facing: facingRight, t: 2 },\n      { position: { x: 1, y: 2 }, facing: facingRight, t: 3 },\n      { position: { x: 2, y: 2 }, facing: facingRight, t: 4 },\n    ]);\n    expect(compressed).toEqual([\n      [0, 0, 0, 1, 0],\n      [0, 2, 1, 0, 2],\n      [2, 2, 1, 0, 4],\n    ]);\n  });\n});\n"
  },
  {
    "path": "convex/util/geometry.ts",
    "content": "import { Path, PathComponent, Point, Vector, packPathComponent, queryPath } from './types';\n\nexport function distance(p0: Point, p1: Point): number {\n  const dx = p0.x - p1.x;\n  const dy = p0.y - p1.y;\n  return Math.sqrt(dx * dx + dy * dy);\n}\n\nexport function pointsEqual(p0: Point, p1: Point): boolean {\n  return p0.x == p1.x && p0.y == p1.y;\n}\n\nexport function manhattanDistance(p0: Point, p1: Point) {\n  return Math.abs(p0.x - p1.x) + Math.abs(p0.y - p1.y);\n}\n\nexport function pathOverlaps(path: Path, time: number): boolean {\n  if (path.length < 2) {\n    throw new Error(`Invalid path: ${JSON.stringify(path)}`);\n  }\n  const start = queryPath(path, 0);\n  const end = queryPath(path, path.length - 1);\n  return start.t <= time && time <= end.t;\n}\n\nexport function pathPosition(\n  path: Path,\n  time: number,\n): { position: Point; facing: Vector; velocity: number } {\n  if (path.length < 2) {\n    throw new Error(`Invalid path: ${JSON.stringify(path)}`);\n  }\n  const first = queryPath(path, 0);\n  if (time < first.t) {\n    return { position: first.position, facing: first.facing, velocity: 0 };\n  }\n  const last = queryPath(path, path.length - 1);\n  if (last.t < time) {\n    return { position: last.position, facing: last.facing, velocity: 0 };\n  }\n  for (let i = 0; i < path.length - 1; i++) {\n    const segmentStart = queryPath(path, i);\n    const segmentEnd = queryPath(path, i + 1);\n    if (segmentStart.t <= time && time <= segmentEnd.t) {\n      const interp = (time - segmentStart.t) / (segmentEnd.t - segmentStart.t);\n      return {\n        position: {\n          x: segmentStart.position.x + interp * (segmentEnd.position.x - segmentStart.position.x),\n          y: segmentStart.position.y + interp * (segmentEnd.position.y - segmentStart.position.y),\n        },\n        facing: segmentStart.facing,\n        velocity:\n          distance(segmentStart.position, segmentEnd.position) / (segmentEnd.t - segmentStart.t),\n      };\n    }\n  }\n  throw new Error(`Timestamp checks not exhaustive?`);\n}\n\nexport const EPSILON = 0.0001;\n\nexport function vector(p0: Point, p1: Point): Vector {\n  const dx = p1.x - p0.x;\n  const dy = p1.y - p0.y;\n  return { dx, dy };\n}\n\nexport function vectorLength(vector: Vector): number {\n  return Math.sqrt(vector.dx * vector.dx + vector.dy * vector.dy);\n}\n\nexport function normalize(vector: Vector): Vector | null {\n  const len = vectorLength(vector);\n  if (len < EPSILON) {\n    return null;\n  }\n  const { dx, dy } = vector;\n  return {\n    dx: dx / len,\n    dy: dy / len,\n  };\n}\n\nexport function orientationDegrees(vector: Vector): number {\n  if (Math.sqrt(vector.dx * vector.dx + vector.dy * vector.dy) < EPSILON) {\n    throw new Error(`Can't compute the orientation of too small vector ${JSON.stringify(vector)}`);\n  }\n  const twoPi = 2 * Math.PI;\n  const radians = (Math.atan2(vector.dy, vector.dx) + twoPi) % twoPi;\n  return (radians / twoPi) * 360;\n}\n\nexport function compressPath(densePath: PathComponent[]): Path {\n  const packed = densePath.map(packPathComponent);\n  if (densePath.length <= 2) {\n    return densePath.map(packPathComponent);\n  }\n  const out = [packPathComponent(densePath[0])];\n  let last = densePath[0];\n  let candidate;\n  for (const point of densePath.slice(1)) {\n    if (!candidate) {\n      candidate = point;\n      continue;\n    }\n    // We can skip `candidate` if it interpolates cleanly between\n    // `last` and `point`.\n    const { position, facing } = pathPosition(\n      [packPathComponent(last), packPathComponent(point)],\n      candidate.t,\n    );\n    const positionCloseEnough = distance(position, candidate.position) < EPSILON;\n    const facingDifference = {\n      dx: facing.dx - candidate.facing.dx,\n      dy: facing.dy - candidate.facing.dy,\n    };\n    const facingCloseEnough = vectorLength(facingDifference) < EPSILON;\n\n    if (positionCloseEnough && facingCloseEnough) {\n      candidate = point;\n      continue;\n    }\n\n    out.push(packPathComponent(candidate));\n    last = candidate;\n    candidate = point;\n  }\n  if (candidate) {\n    out.push(packPathComponent(candidate));\n  }\n  return out;\n}\n"
  },
  {
    "path": "convex/util/isSimpleObject.ts",
    "content": "export function isSimpleObject(value: unknown) {\n  const isObject = typeof value === 'object';\n  const prototype = Object.getPrototypeOf(value);\n  const isSimple =\n    prototype === null ||\n    prototype === Object.prototype ||\n    // Objects generated from other contexts (e.g. across Node.js `vm` modules) will not satisfy the previous\n    // conditions but are still simple objects.\n    prototype?.constructor?.name === 'Object';\n  return isObject && isSimple;\n}\n"
  },
  {
    "path": "convex/util/llm.ts",
    "content": "// That's right! No imports and no dependencies 🤯\n\nconst OPENAI_EMBEDDING_DIMENSION = 1536;\nconst TOGETHER_EMBEDDING_DIMENSION = 768;\nconst OLLAMA_EMBEDDING_DIMENSION = 1024;\n\nexport const EMBEDDING_DIMENSION: number = OLLAMA_EMBEDDING_DIMENSION;\n\nexport function detectMismatchedLLMProvider() {\n  switch (EMBEDDING_DIMENSION) {\n    case OPENAI_EMBEDDING_DIMENSION:\n      if (!process.env.OPENAI_API_KEY) {\n        throw new Error(\n          \"Are you trying to use OpenAI? If so, run: npx convex env set OPENAI_API_KEY 'your-key'\",\n        );\n      }\n      break;\n    case TOGETHER_EMBEDDING_DIMENSION:\n      if (!process.env.TOGETHER_API_KEY) {\n        throw new Error(\n          \"Are you trying to use Together.ai? If so, run: npx convex env set TOGETHER_API_KEY 'your-key'\",\n        );\n      }\n      break;\n    case OLLAMA_EMBEDDING_DIMENSION:\n      break;\n    default:\n      if (!process.env.LLM_API_URL) {\n        throw new Error(\n          \"Are you trying to use a custom cloud-hosted LLM? If so, run: npx convex env set LLM_API_URL 'your-url'\",\n        );\n      }\n      break;\n  }\n}\n\nexport interface LLMConfig {\n  provider: 'openai' | 'together' | 'ollama' | 'custom';\n  url: string; // Should not have a trailing slash\n  chatModel: string;\n  embeddingModel: string;\n  stopWords: string[];\n  apiKey: string | undefined;\n}\n\nexport function getLLMConfig(): LLMConfig {\n  let provider = process.env.LLM_PROVIDER;\n  if (provider ? provider === 'openai' : process.env.OPENAI_API_KEY) {\n    if (EMBEDDING_DIMENSION !== OPENAI_EMBEDDING_DIMENSION) {\n      throw new Error('EMBEDDING_DIMENSION must be 1536 for OpenAI');\n    }\n    return {\n      provider: 'openai',\n      url: 'https://api.openai.com',\n      chatModel: process.env.OPENAI_CHAT_MODEL ?? 'gpt-4o-mini',\n      embeddingModel: process.env.OPENAI_EMBEDDING_MODEL ?? 'text-embedding-ada-002',\n      stopWords: [],\n      apiKey: process.env.OPENAI_API_KEY,\n    };\n  }\n  if (process.env.TOGETHER_API_KEY) {\n    if (EMBEDDING_DIMENSION !== TOGETHER_EMBEDDING_DIMENSION) {\n      throw new Error('EMBEDDING_DIMENSION must be 768 for Together.ai');\n    }\n    return {\n      provider: 'together',\n      url: 'https://api.together.xyz',\n      chatModel: process.env.TOGETHER_CHAT_MODEL ?? 'meta-llama/Llama-3-8b-chat-hf',\n      embeddingModel:\n        process.env.TOGETHER_EMBEDDING_MODEL ?? 'togethercomputer/m2-bert-80M-8k-retrieval',\n      stopWords: ['<|eot_id|>'],\n      apiKey: process.env.TOGETHER_API_KEY,\n    };\n  }\n  if (process.env.LLM_API_URL) {\n    const apiKey = process.env.LLM_API_KEY;\n    const url = process.env.LLM_API_URL;\n    const chatModel = process.env.LLM_MODEL;\n    if (!chatModel) throw new Error('LLM_MODEL is required');\n    const embeddingModel = process.env.LLM_EMBEDDING_MODEL;\n    if (!embeddingModel) throw new Error('LLM_EMBEDDING_MODEL is required');\n    return {\n      provider: 'custom',\n      url,\n      chatModel,\n      embeddingModel,\n      stopWords: [],\n      apiKey,\n    };\n  }\n  // Assume Ollama\n  if (EMBEDDING_DIMENSION !== OLLAMA_EMBEDDING_DIMENSION) {\n    detectMismatchedLLMProvider();\n    throw new Error(\n      `Unknown EMBEDDING_DIMENSION ${EMBEDDING_DIMENSION} found` +\n        `. See convex/util/llm.ts for details.`,\n    );\n  }\n  // Alternative embedding model:\n  // embeddingModel: 'llama3'\n  // const OLLAMA_EMBEDDING_DIMENSION = 4096,\n  return {\n    provider: 'ollama',\n    url: process.env.OLLAMA_HOST ?? 'http://127.0.0.1:11434',\n    chatModel: process.env.OLLAMA_MODEL ?? 'llama3',\n    embeddingModel: process.env.OLLAMA_EMBEDDING_MODEL ?? 'mxbai-embed-large',\n    stopWords: ['<|eot_id|>'],\n    apiKey: undefined,\n  };\n}\n\nconst AuthHeaders = (): Record<string, string> =>\n  getLLMConfig().apiKey\n    ? {\n        Authorization: 'Bearer ' + getLLMConfig().apiKey,\n      }\n    : {};\n\n// Overload for non-streaming\nexport async function chatCompletion(\n  body: Omit<CreateChatCompletionRequest, 'model'> & {\n    model?: CreateChatCompletionRequest['model'];\n  } & {\n    stream?: false | null | undefined;\n  },\n): Promise<{ content: string; retries: number; ms: number }>;\n// Overload for streaming\nexport async function chatCompletion(\n  body: Omit<CreateChatCompletionRequest, 'model'> & {\n    model?: CreateChatCompletionRequest['model'];\n  } & {\n    stream?: true;\n  },\n): Promise<{ content: ChatCompletionContent; retries: number; ms: number }>;\nexport async function chatCompletion(\n  body: Omit<CreateChatCompletionRequest, 'model'> & {\n    model?: CreateChatCompletionRequest['model'];\n  },\n) {\n  const config = getLLMConfig();\n  body.model = body.model ?? config.chatModel;\n  const stopWords = body.stop ? (typeof body.stop === 'string' ? [body.stop] : body.stop) : [];\n  if (config.stopWords) stopWords.push(...config.stopWords);\n  console.log(body);\n  const {\n    result: content,\n    retries,\n    ms,\n  } = await retryWithBackoff(async () => {\n    const result = await fetch(config.url + '/v1/chat/completions', {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        ...AuthHeaders(),\n      },\n\n      body: JSON.stringify(body),\n    });\n    if (!result.ok) {\n      const error = await result.text();\n      console.error({ error });\n      if (result.status === 404 && config.provider === 'ollama') {\n        await tryPullOllama(body.model!, error);\n      }\n      throw {\n        retry: result.status === 429 || result.status >= 500,\n        error: new Error(`Chat completion failed with code ${result.status}: ${error}`),\n      };\n    }\n    if (body.stream) {\n      return new ChatCompletionContent(result.body!, stopWords);\n    } else {\n      const json = (await result.json()) as CreateChatCompletionResponse;\n      const content = json.choices[0].message?.content;\n      if (content === undefined) {\n        throw new Error('Unexpected result from OpenAI: ' + JSON.stringify(json));\n      }\n      console.log(content);\n      return content;\n    }\n  });\n\n  return {\n    content,\n    retries,\n    ms,\n  };\n}\n\nexport async function tryPullOllama(model: string, error: string) {\n  if (error.includes('try pulling')) {\n    console.error('Embedding model not found, pulling from Ollama');\n    const pullResp = await fetch(getLLMConfig().url + '/api/pull', {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify({ name: model }),\n    });\n    console.log('Pull response', await pullResp.text());\n    throw { retry: true, error: `Dynamically pulled model. Original error: ${error}` };\n  }\n}\n\nexport async function fetchEmbeddingBatch(texts: string[]) {\n  const config = getLLMConfig();\n  if (config.provider === 'ollama') {\n    return {\n      ollama: true as const,\n      embeddings: await Promise.all(\n        texts.map(async (t) => (await ollamaFetchEmbedding(t)).embedding),\n      ),\n    };\n  }\n  const {\n    result: json,\n    retries,\n    ms,\n  } = await retryWithBackoff(async () => {\n    const result = await fetch(config.url + '/v1/embeddings', {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        ...AuthHeaders(),\n      },\n\n      body: JSON.stringify({\n        model: config.embeddingModel,\n        input: texts.map((text) => text.replace(/\\n/g, ' ')),\n      }),\n    });\n    if (!result.ok) {\n      throw {\n        retry: result.status === 429 || result.status >= 500,\n        error: new Error(`Embedding failed with code ${result.status}: ${await result.text()}`),\n      };\n    }\n    return (await result.json()) as CreateEmbeddingResponse;\n  });\n  if (json.data.length !== texts.length) {\n    console.error(json);\n    throw new Error('Unexpected number of embeddings');\n  }\n  const allembeddings = json.data;\n  allembeddings.sort((a, b) => a.index - b.index);\n  return {\n    ollama: false as const,\n    embeddings: allembeddings.map(({ embedding }) => embedding),\n    usage: json.usage?.total_tokens,\n    retries,\n    ms,\n  };\n}\n\nexport async function fetchEmbedding(text: string) {\n  const { embeddings, ...stats } = await fetchEmbeddingBatch([text]);\n  return { embedding: embeddings[0], ...stats };\n}\n\nexport async function fetchModeration(content: string) {\n  const { result: flagged } = await retryWithBackoff(async () => {\n    const result = await fetch(getLLMConfig().url + '/v1/moderations', {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        ...AuthHeaders(),\n      },\n\n      body: JSON.stringify({\n        input: content,\n      }),\n    });\n    if (!result.ok) {\n      throw {\n        retry: result.status === 429 || result.status >= 500,\n        error: new Error(`Embedding failed with code ${result.status}: ${await result.text()}`),\n      };\n    }\n    return (await result.json()) as { results: { flagged: boolean }[] };\n  });\n  return flagged;\n}\n\n// Retry after this much time, based on the retry number.\nconst RETRY_BACKOFF = [1000, 10_000, 20_000]; // In ms\nconst RETRY_JITTER = 100; // In ms\ntype RetryError = { retry: boolean; error: any };\n\nexport async function retryWithBackoff<T>(\n  fn: () => Promise<T>,\n): Promise<{ retries: number; result: T; ms: number }> {\n  let i = 0;\n  for (; i <= RETRY_BACKOFF.length; i++) {\n    try {\n      const start = Date.now();\n      const result = await fn();\n      const ms = Date.now() - start;\n      return { result, retries: i, ms };\n    } catch (e) {\n      const retryError = e as RetryError;\n      if (i < RETRY_BACKOFF.length) {\n        if (retryError.retry) {\n          console.log(\n            `Attempt ${i + 1} failed, waiting ${RETRY_BACKOFF[i]}ms to retry...`,\n            Date.now(),\n          );\n          await new Promise((resolve) =>\n            setTimeout(resolve, RETRY_BACKOFF[i] + RETRY_JITTER * Math.random()),\n          );\n          continue;\n        }\n      }\n      if (retryError.error) throw retryError.error;\n      else throw e;\n    }\n  }\n  throw new Error('Unreachable');\n}\n\n// Lifted from openai's package\nexport interface LLMMessage {\n  /**\n   * The contents of the message. `content` is required for all messages, and may be\n   * null for assistant messages with function calls.\n   */\n  content: string | null;\n\n  /**\n   * The role of the messages author. One of `system`, `user`, `assistant`, or\n   * `function`.\n   */\n  role: 'system' | 'user' | 'assistant' | 'function';\n\n  /**\n   * The name of the author of this message. `name` is required if role is\n   * `function`, and it should be the name of the function whose response is in the\n   * `content`. May contain a-z, A-Z, 0-9, and underscores, with a maximum length of\n   * 64 characters.\n   */\n  name?: string;\n\n  /**\n   * The name and arguments of a function that should be called, as generated by the model.\n   */\n  function_call?: {\n    // The name of the function to call.\n    name: string;\n    /**\n     * The arguments to call the function with, as generated by the model in\n     * JSON format. Note that the model does not always generate valid JSON,\n     * and may hallucinate parameters not defined by your function schema.\n     * Validate the arguments in your code before calling your function.\n     */\n    arguments: string;\n  };\n}\n\n// Non-streaming chat completion response\ninterface CreateChatCompletionResponse {\n  id: string;\n  object: string;\n  created: number;\n  model: string;\n  choices: {\n    index?: number;\n    message?: {\n      role: 'system' | 'user' | 'assistant';\n      content: string;\n    };\n    finish_reason?: string;\n  }[];\n  usage?: {\n    completion_tokens: number;\n\n    prompt_tokens: number;\n\n    total_tokens: number;\n  };\n}\n\ninterface CreateEmbeddingResponse {\n  data: {\n    index: number;\n    object: string;\n    embedding: number[];\n  }[];\n  model: string;\n  object: string;\n  usage: {\n    prompt_tokens: number;\n    total_tokens: number;\n  };\n}\n\nexport interface CreateChatCompletionRequest {\n  /**\n   * ID of the model to use.\n   * @type {string}\n   * @memberof CreateChatCompletionRequest\n   */\n  model: string;\n  // | 'gpt-4'\n  // | 'gpt-4-0613'\n  // | 'gpt-4-32k'\n  // | 'gpt-4-32k-0613'\n  // | 'gpt-3.5-turbo'; // <- our default\n  /**\n   * The messages to generate chat completions for, in the chat format:\n   * https://platform.openai.com/docs/guides/chat/introduction\n   * @type {Array<ChatCompletionRequestMessage>}\n   * @memberof CreateChatCompletionRequest\n   */\n  messages: LLMMessage[];\n  /**\n   * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.  We generally recommend altering this or `top_p` but not both.\n   * @type {number}\n   * @memberof CreateChatCompletionRequest\n   */\n  temperature?: number | null;\n  /**\n   * An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.  We generally recommend altering this or `temperature` but not both.\n   * @type {number}\n   * @memberof CreateChatCompletionRequest\n   */\n  top_p?: number | null;\n  /**\n   * How many chat completion choices to generate for each input message.\n   * @type {number}\n   * @memberof CreateChatCompletionRequest\n   */\n  n?: number | null;\n  /**\n   * If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message.\n   * @type {boolean}\n   * @memberof CreateChatCompletionRequest\n   */\n  stream?: boolean | null;\n  /**\n   *\n   * @type {CreateChatCompletionRequestStop}\n   * @memberof CreateChatCompletionRequest\n   */\n  stop?: Array<string> | string;\n  /**\n   * The maximum number of tokens allowed for the generated answer. By default,\n   * the number of tokens the model can return will be (4096 - prompt tokens).\n   * @type {number}\n   * @memberof CreateChatCompletionRequest\n   */\n  max_tokens?: number;\n  /**\n   * Number between -2.0 and 2.0. Positive values penalize new tokens based on\n   * whether they appear in the text so far, increasing the model\\'s likelihood\n   * to talk about new topics. See more information about frequency and\n   * presence penalties:\n   * https://platform.openai.com/docs/api-reference/parameter-details\n   * @type {number}\n   * @memberof CreateChatCompletionRequest\n   */\n  presence_penalty?: number | null;\n  /**\n   * Number between -2.0 and 2.0. Positive values penalize new tokens based on\n   * their existing frequency in the text so far, decreasing the model\\'s\n   * likelihood to repeat the same line verbatim. See more information about\n   * presence penalties:\n   * https://platform.openai.com/docs/api-reference/parameter-details\n   * @type {number}\n   * @memberof CreateChatCompletionRequest\n   */\n  frequency_penalty?: number | null;\n  /**\n   * Modify the likelihood of specified tokens appearing in the completion.\n   * Accepts a json object that maps tokens (specified by their token ID in the\n   * tokenizer) to an associated bias value from -100 to 100. Mathematically,\n   * the bias is added to the logits generated by the model prior to sampling.\n   * The exact effect will vary per model, but values between -1 and 1 should\n   * decrease or increase likelihood of selection; values like -100 or 100\n   * should result in a ban or exclusive selection of the relevant token.\n   * @type {object}\n   * @memberof CreateChatCompletionRequest\n   */\n  logit_bias?: object | null;\n  /**\n   * A unique identifier representing your end-user, which can help OpenAI to\n   * monitor and detect abuse. Learn more:\n   * https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids\n   * @type {string}\n   * @memberof CreateChatCompletionRequest\n   */\n  user?: string;\n  tools?: {\n    // The type of the tool. Currently, only function is supported.\n    type: 'function';\n    function: {\n      /**\n       * The name of the function to be called. Must be a-z, A-Z, 0-9, or\n       * contain underscores and dashes, with a maximum length of 64.\n       */\n      name: string;\n      /**\n       * A description of what the function does, used by the model to choose\n       * when and how to call the function.\n       */\n      description?: string;\n      /**\n       * The parameters the functions accepts, described as a JSON Schema\n       * object. See the guide[1] for examples, and the JSON Schema reference[2]\n       * for documentation about the format.\n       * [1]: https://platform.openai.com/docs/guides/gpt/function-calling\n       * [2]: https://json-schema.org/understanding-json-schema/\n       * To describe a function that accepts no parameters, provide the value\n       * {\"type\": \"object\", \"properties\": {}}.\n       */\n      parameters: object;\n    };\n  }[];\n  /**\n   * Controls which (if any) function is called by the model. `none` means the\n   * model will not call a function and instead generates a message.\n   * `auto` means the model can pick between generating a message or calling a\n   * function. Specifying a particular function via\n   * {\"type: \"function\", \"function\": {\"name\": \"my_function\"}} forces the model\n   * to call that function.\n   *\n   * `none` is the default when no functions are present.\n   * `auto` is the default if functions are present.\n   */\n  tool_choice?:\n    | 'none' // none means the model will not call a function and instead generates a message.\n    | 'auto' // auto means the model can pick between generating a message or calling a function.\n    // Specifies a tool the model should use. Use to force the model to call\n    // a specific function.\n    | {\n        // The type of the tool. Currently, only function is supported.\n        type: 'function';\n        function: { name: string };\n      };\n  // Replaced by \"tools\"\n  // functions?: {\n  //   /**\n  //    * The name of the function to be called. Must be a-z, A-Z, 0-9, or\n  //    * contain underscores and dashes, with a maximum length of 64.\n  //    */\n  //   name: string;\n  //   /**\n  //    * A description of what the function does, used by the model to choose\n  //    * when and how to call the function.\n  //    */\n  //   description?: string;\n  //   /**\n  //    * The parameters the functions accepts, described as a JSON Schema\n  //    * object. See the guide[1] for examples, and the JSON Schema reference[2]\n  //    * for documentation about the format.\n  //    * [1]: https://platform.openai.com/docs/guides/gpt/function-calling\n  //    * [2]: https://json-schema.org/understanding-json-schema/\n  //    * To describe a function that accepts no parameters, provide the value\n  //    * {\"type\": \"object\", \"properties\": {}}.\n  //    */\n  //   parameters: object;\n  // }[];\n  // /**\n  //  * Controls how the model responds to function calls. \"none\" means the model\n  //  * does not call a function, and responds to the end-user. \"auto\" means the\n  //  * model can pick between an end-user or calling a function. Specifying a\n  //  * particular function via {\"name\":\\ \"my_function\"} forces the model to call\n  //  *  that function.\n  //  * - \"none\" is the default when no functions are present.\n  //  * - \"auto\" is the default if functions are present.\n  //  */\n  // function_call?: 'none' | 'auto' | { name: string };\n  /**\n   * An object specifying the format that the model must output.\n   *\n   * Setting to { \"type\": \"json_object\" } enables JSON mode, which guarantees\n   * the message the model generates is valid JSON.\n   * *Important*: when using JSON mode, you must also instruct the model to\n   * produce JSON yourself via a system or user message. Without this, the model\n   * may generate an unending stream of whitespace until the generation reaches\n   * the token limit, resulting in a long-running and seemingly \"stuck\" request.\n   * Also note that the message content may be partially cut off if\n   * finish_reason=\"length\", which indicates the generation exceeded max_tokens\n   * or the conversation exceeded the max context length.\n   */\n  response_format?: { type: 'text' | 'json_object' };\n}\n\n// Checks whether a suffix of s1 is a prefix of s2. For example,\n// ('Hello', 'Kira:') -> false\n// ('Hello Kira', 'Kira:') -> true\nconst suffixOverlapsPrefix = (s1: string, s2: string) => {\n  for (let i = 1; i <= Math.min(s1.length, s2.length); i++) {\n    const suffix = s1.substring(s1.length - i);\n    const prefix = s2.substring(0, i);\n    if (suffix === prefix) {\n      return true;\n    }\n  }\n  return false;\n};\n\nexport class ChatCompletionContent {\n  private readonly body: ReadableStream<Uint8Array>;\n  private readonly stopWords: string[];\n\n  constructor(body: ReadableStream<Uint8Array>, stopWords: string[]) {\n    this.body = body;\n    this.stopWords = stopWords;\n  }\n\n  async *readInner() {\n    for await (const data of this.splitStream(this.body)) {\n      if (data.startsWith('data: ')) {\n        try {\n          const json = JSON.parse(data.substring('data: '.length)) as {\n            choices: { delta: { content?: string } }[];\n          };\n          if (json.choices[0].delta.content) {\n            yield json.choices[0].delta.content;\n          }\n        } catch (e) {\n          // e.g. the last chunk is [DONE] which is not valid JSON.\n        }\n      }\n    }\n  }\n\n  // stop words in OpenAI api don't always work.\n  // So we have to truncate on our side.\n  async *read() {\n    let lastFragment = '';\n    for await (const data of this.readInner()) {\n      lastFragment += data;\n      let hasOverlap = false;\n      for (const stopWord of this.stopWords) {\n        const idx = lastFragment.indexOf(stopWord);\n        if (idx >= 0) {\n          yield lastFragment.substring(0, idx);\n          return;\n        }\n        if (suffixOverlapsPrefix(lastFragment, stopWord)) {\n          hasOverlap = true;\n        }\n      }\n      if (hasOverlap) continue;\n      yield lastFragment;\n      lastFragment = '';\n    }\n    yield lastFragment;\n  }\n\n  async readAll() {\n    let allContent = '';\n    for await (const chunk of this.read()) {\n      allContent += chunk;\n    }\n    return allContent;\n  }\n\n  async *splitStream(stream: ReadableStream<Uint8Array>) {\n    const reader = stream.getReader();\n    let lastFragment = '';\n    try {\n      while (true) {\n        const { value, done } = await reader.read();\n        if (done) {\n          // Flush the last fragment now that we're done\n          if (lastFragment !== '') {\n            yield lastFragment;\n          }\n          break;\n        }\n        const data = new TextDecoder().decode(value);\n        lastFragment += data;\n        const parts = lastFragment.split('\\n\\n');\n        // Yield all except for the last part\n        for (let i = 0; i < parts.length - 1; i += 1) {\n          yield parts[i];\n        }\n        // Save the last part as the new last fragment\n        lastFragment = parts[parts.length - 1];\n      }\n    } finally {\n      reader.releaseLock();\n    }\n  }\n}\n\nexport async function ollamaFetchEmbedding(text: string) {\n  const config = getLLMConfig();\n  const { result } = await retryWithBackoff(async () => {\n    const resp = await fetch(config.url + '/api/embeddings', {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify({ model: config.embeddingModel, prompt: text }),\n    });\n    if (resp.status === 404) {\n      const error = await resp.text();\n      await tryPullOllama(config.embeddingModel, error);\n      throw new Error(`Failed to fetch embeddings: ${resp.status}`);\n    }\n    return (await resp.json()).embedding as number[];\n  });\n  return { embedding: result };\n}\n"
  },
  {
    "path": "convex/util/minheap.test.ts",
    "content": "import { MinHeap } from './minheap';\n\ndescribe('MinHeap', () => {\n  const compareNumbers = (a: number, b: number): boolean => a > b;\n\n  test('should initialize an empty heap', () => {\n    const heap = MinHeap(compareNumbers);\n    expect(heap.length()).toBe(0);\n    expect(heap.peek()).toBeUndefined();\n  });\n\n  test('should insert values correctly and maintain the min property', () => {\n    const heap = MinHeap(compareNumbers);\n    heap.push(3);\n    heap.push(1);\n    heap.push(4);\n    heap.push(2);\n\n    expect(heap.peek()).toBe(1);\n    expect(heap.length()).toBe(4);\n  });\n\n  test('should pop values correctly and maintain the min property', () => {\n    const heap = MinHeap(compareNumbers);\n    heap.push(3);\n    heap.push(1);\n    heap.push(4);\n    heap.push(2);\n\n    expect(heap.pop()).toBe(1);\n    expect(heap.length()).toBe(3);\n    expect(heap.peek()).toBe(2);\n\n    expect(heap.pop()).toBe(2);\n    expect(heap.length()).toBe(2);\n    expect(heap.peek()).toBe(3);\n  });\n\n  test('should handle popping from an empty heap', () => {\n    const heap = MinHeap(compareNumbers);\n    expect(heap.pop()).toBeUndefined();\n    expect(heap.length()).toBe(0);\n    expect(heap.peek()).toBeUndefined();\n  });\n\n  test('should handle peeking from an empty heap', () => {\n    const heap = MinHeap(compareNumbers);\n    expect(heap.peek()).toBeUndefined();\n  });\n\n  test('should handle custom comparison functions', () => {\n    const compareStringsByLength = (a: string, b: string): boolean => a.length > b.length;\n    const heap = MinHeap(compareStringsByLength);\n    heap.push('apple');\n    heap.push('banana');\n    heap.push('cherry');\n\n    expect(heap.peek()).toBe('apple');\n    heap.push('kiwi');\n    expect(heap.peek()).toBe('kiwi');\n  });\n});\n"
  },
  {
    "path": "convex/util/minheap.ts",
    "content": "// Basic 1-indexed minheap implementation\nexport function MinHeap<T>(compare: (a: T, b: T) => boolean) {\n  const tree = [null as T];\n  let endIndex = 1;\n  return {\n    peek: (): T | undefined => tree[1],\n    length: () => endIndex - 1,\n    push: (newValue: T) => {\n      let destinationIndex = endIndex++;\n      let nextToCheck;\n      while ((nextToCheck = destinationIndex >> 1) > 0) {\n        const existing = tree[nextToCheck];\n        if (compare(newValue, existing)) break;\n        tree[destinationIndex] = existing;\n        destinationIndex = nextToCheck;\n      }\n      tree[destinationIndex] = newValue;\n    },\n    pop: () => {\n      if (endIndex == 1) return undefined;\n      endIndex--;\n      const value = tree[1];\n      const lastValue = tree[endIndex];\n      let destinationIndex = 1;\n      let nextToCheck;\n      while ((nextToCheck = destinationIndex << 1) < endIndex) {\n        if (nextToCheck + 1 <= endIndex && compare(tree[nextToCheck], tree[nextToCheck + 1]))\n          nextToCheck++;\n        const existing = tree[nextToCheck];\n        if (compare(existing, lastValue)) break;\n        tree[destinationIndex] = existing;\n        destinationIndex = nextToCheck;\n      }\n      tree[destinationIndex] = lastValue;\n      return value;\n    },\n  };\n}\n"
  },
  {
    "path": "convex/util/object.ts",
    "content": "export function parseMap<Id, Serialized, Parsed>(\n  records: Serialized[],\n  constructor: new (r: Serialized) => Parsed,\n  getId: (r: Parsed) => Id,\n): Map<Id, Parsed> {\n  const out = new Map();\n  for (const record of records) {\n    const parsed = new constructor(record);\n    const id = getId(parsed);\n    if (out.has(id)) {\n      throw new Error(`Duplicate ID ${id}`);\n    }\n    out.set(id, parsed);\n  }\n  return out;\n}\n\nexport function serializeMap<Serialized, T extends { serialize(): Serialized }>(\n  map: Map<string, T>,\n): Serialized[] {\n  return [...map.values()].map((v) => v.serialize());\n}\n"
  },
  {
    "path": "convex/util/sleep.ts",
    "content": "export async function sleep(ms: number) {\n  return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"
  },
  {
    "path": "convex/util/types.test.ts",
    "content": "import { Path, PathComponent, packPathComponent, queryPath, unpackPathComponent } from \"./types\";\n\ndescribe('queryPath', () => {\n  it('should return the correct path component', () => {\n    const p: Path = [\n      [1, 2, 3, 4, 5],\n      [6, 7, 8, 9, 10],\n      [11, 12, 13, 14, 15]\n    ];\n    const expected = {\n      position: { x: 6, y: 7 },\n      facing: { dx: 8, dy: 9 },\n      t: 10,\n    };\n    expect(queryPath(p, 1)).toEqual(expected);\n  });\n});\n\ndescribe('packPathComponent', () => {\n  it('should correctly pack a path component', () => {\n    const p: PathComponent = {\n      position: { x: 10, y: 20 },\n      facing: { dx: 3, dy: 4 },\n      t: 5,\n    };\n    const expected = [10, 20, 3, 4, 5];\n    expect(packPathComponent(p)).toEqual(expected);\n  })\n});\n\ndescribe('unpackPathComponent', () => {\n  it('should unpack a path component with positive values', () => {\n    const input: [number, number, number, number, number] = [10, 20, 3, 4, 5];\n    const expected = {\n      position: { x: 10, y: 20 },\n      facing: { dx: 3, dy: 4 },\n      t: 5,\n    }\n    const actual = unpackPathComponent(input);\n    expect(actual).toEqual(expected);\n  });\n});"
  },
  {
    "path": "convex/util/types.ts",
    "content": "import { Infer, v } from 'convex/values';\n\nexport const point = v.object({\n  x: v.number(),\n  y: v.number(),\n});\nexport type Point = Infer<typeof point>;\n\nexport const vector = v.object({\n  dx: v.number(),\n  dy: v.number(),\n});\nexport type Vector = Infer<typeof vector>;\n\n// Paths are arrays of [x, y, dx, dy, t] tuples;\nexport const path = v.array(v.array(v.number()));\nexport type Path = [number, number, number, number, number][];\n\nexport type PathComponent = { position: Point; facing: Vector; t: number };\n\nexport function queryPath(p: Path, at: number): PathComponent {\n  return unpackPathComponent(p[at]);\n}\nexport function packPathComponent(p: PathComponent): [number, number, number, number, number] {\n  return [p.position.x, p.position.y, p.facing.dx, p.facing.dy, p.t];\n}\nexport function unpackPathComponent(p: [number, number, number, number, number]): PathComponent {\n  return {\n    position: { x: p[0], y: p[1] },\n    facing: { dx: p[2], dy: p[3] },\n    t: p[4],\n  };\n}\n"
  },
  {
    "path": "convex/util/xxhash.ts",
    "content": "/*\nFrom https://github.com/Jason3S/xxhash\n\nMIT License\n\nCopyright (c) 2019 Jason Dent\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\nconst PRIME32_1 = 2654435761;\nconst PRIME32_2 = 2246822519;\nconst PRIME32_3 = 3266489917;\nconst PRIME32_4 = 668265263;\nconst PRIME32_5 = 374761393;\n\nexport function toUtf8(text: string): Uint8Array {\n  const bytes: number[] = [];\n  for (let i = 0, n = text.length; i < n; ++i) {\n    const c = text.charCodeAt(i);\n    if (c < 0x80) {\n      bytes.push(c);\n    } else if (c < 0x800) {\n      bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));\n    } else if (c < 0xd800 || c >= 0xe000) {\n      bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));\n    } else {\n      const cp = 0x10000 + (((c & 0x3ff) << 10) | (text.charCodeAt(++i) & 0x3ff));\n      bytes.push(\n        0xf0 | ((cp >> 18) & 0x7),\n        0x80 | ((cp >> 12) & 0x3f),\n        0x80 | ((cp >> 6) & 0x3f),\n        0x80 | (cp & 0x3f),\n      );\n    }\n  }\n  return new Uint8Array(bytes);\n}\n\n/**\n *\n * @param buffer - byte array or string\n * @param seed - optional seed (32-bit unsigned);\n */\nexport function xxHash32(buffer: Uint8Array | string, seed = 0): number {\n  buffer = typeof buffer === 'string' ? toUtf8(buffer) : buffer;\n  const b = buffer;\n\n  /*\n        Step 1. Initialize internal accumulators\n        Each accumulator gets an initial value based on optional seed input. Since the seed is optional, it can be 0.\n\n        ```\n            u32 acc1 = seed + PRIME32_1 + PRIME32_2;\n            u32 acc2 = seed + PRIME32_2;\n            u32 acc3 = seed + 0;\n            u32 acc4 = seed - PRIME32_1;\n        ```\n        Special case : input is less than 16 bytes\n        When input is too small (< 16 bytes), the algorithm will not process any stripe. Consequently, it will not\n        make use of parallel accumulators.\n\n        In which case, a simplified initialization is performed, using a single accumulator :\n\n        u32 acc  = seed + PRIME32_5;\n        The algorithm then proceeds directly to step 4.\n    */\n\n  let acc = (seed + PRIME32_5) & 0xffffffff;\n  let offset = 0;\n\n  if (b.length >= 16) {\n    const accN = [\n      (seed + PRIME32_1 + PRIME32_2) & 0xffffffff,\n      (seed + PRIME32_2) & 0xffffffff,\n      (seed + 0) & 0xffffffff,\n      (seed - PRIME32_1) & 0xffffffff,\n    ];\n\n    /*\n            Step 2. Process stripes\n            A stripe is a contiguous segment of 16 bytes. It is evenly divided into 4 lanes, of 4 bytes each.\n            The first lane is used to update accumulator 1, the second lane is used to update accumulator 2, and so on.\n\n            Each lane read its associated 32-bit value using little-endian convention.\n\n            For each {lane, accumulator}, the update process is called a round, and applies the following formula :\n\n            ```\n            accN = accN + (laneN * PRIME32_2);\n            accN = accN <<< 13;\n            accN = accN * PRIME32_1;\n            ```\n\n            This shuffles the bits so that any bit from input lane impacts several bits in output accumulator.\n            All operations are performed modulo 2^32.\n\n            Input is consumed one full stripe at a time. Step 2 is looped as many times as necessary to consume\n            the whole input, except the last remaining bytes which cannot form a stripe (< 16 bytes). When that\n            happens, move to step 3.\n        */\n\n    const b = buffer;\n    const limit = b.length - 16;\n    let lane = 0;\n    for (offset = 0; (offset & 0xfffffff0) <= limit; offset += 4) {\n      const i = offset;\n      const laneN0 = (b[i + 0] as any) + ((b[i + 1] as any) << 8);\n      const laneN1 = (b[i + 2] as any) + ((b[i + 3] as any) << 8);\n      const laneNP = laneN0 * PRIME32_2 + ((laneN1 * PRIME32_2) << 16);\n      let acc = (accN[lane] + laneNP) & 0xffffffff;\n      acc = (acc << 13) | (acc >>> 19);\n      const acc0 = acc & 0xffff;\n      const acc1 = acc >>> 16;\n      accN[lane] = (acc0 * PRIME32_1 + ((acc1 * PRIME32_1) << 16)) & 0xffffffff;\n      lane = (lane + 1) & 0x3;\n    }\n\n    /*\n            Step 3. Accumulator convergence\n            All 4 lane accumulators from previous steps are merged to produce a single remaining accumulator\n            of same width (32-bit). The associated formula is as follows :\n\n            ```\n            acc = (acc1 <<< 1) + (acc2 <<< 7) + (acc3 <<< 12) + (acc4 <<< 18);\n            ```\n        */\n    acc =\n      (((accN[0] << 1) | (accN[0] >>> 31)) +\n        ((accN[1] << 7) | (accN[1] >>> 25)) +\n        ((accN[2] << 12) | (accN[2] >>> 20)) +\n        ((accN[3] << 18) | (accN[3] >>> 14))) &\n      0xffffffff;\n  }\n\n  /*\n        Step 4. Add input length\n        The input total length is presumed known at this stage. This step is just about adding the length to\n        accumulator, so that it participates to final mixing.\n\n        ```\n        acc = acc + (u32)inputLength;\n        ```\n    */\n  acc = (acc + buffer.length) & 0xffffffff;\n\n  /*\n        Step 5. Consume remaining input\n        There may be up to 15 bytes remaining to consume from the input. The final stage will digest them according\n        to following pseudo-code :\n        ```\n        while (remainingLength >= 4) {\n            lane = read_32bit_little_endian(input_ptr);\n            acc = acc + lane * PRIME32_3;\n            acc = (acc <<< 17) * PRIME32_4;\n            input_ptr += 4; remainingLength -= 4;\n        }\n        ```\n        This process ensures that all input bytes are present in the final mix.\n    */\n\n  const limit = buffer.length - 4;\n  for (; offset <= limit; offset += 4) {\n    const i = offset;\n    const laneN0 = (b[i + 0] as any) + ((b[i + 1] as any) << 8);\n    const laneN1 = (b[i + 2] as any) + ((b[i + 3] as any) << 8);\n    const laneP = laneN0 * PRIME32_3 + ((laneN1 * PRIME32_3) << 16);\n    acc = (acc + laneP) & 0xffffffff;\n    acc = (acc << 17) | (acc >>> 15);\n    acc = ((acc & 0xffff) * PRIME32_4 + (((acc >>> 16) * PRIME32_4) << 16)) & 0xffffffff;\n  }\n\n  /*\n        ```\n        while (remainingLength >= 1) {\n            lane = read_byte(input_ptr);\n            acc = acc + lane * PRIME32_5;\n            acc = (acc <<< 11) * PRIME32_1;\n            input_ptr += 1; remainingLength -= 1;\n        }\n        ```\n    */\n\n  for (; offset < b.length; ++offset) {\n    const lane = b[offset];\n    acc = acc + (lane as any) * PRIME32_5;\n    acc = (acc << 11) | (acc >>> 21);\n    acc = ((acc & 0xffff) * PRIME32_1 + (((acc >>> 16) * PRIME32_1) << 16)) & 0xffffffff;\n  }\n\n  /*\n        Step 6. Final mix (avalanche)\n        The final mix ensures that all input bits have a chance to impact any bit in the output digest,\n        resulting in an unbiased distribution. This is also called avalanche effect.\n        ```\n        acc = acc xor (acc >> 15);\n        acc = acc * PRIME32_2;\n        acc = acc xor (acc >> 13);\n        acc = acc * PRIME32_3;\n        acc = acc xor (acc >> 16);\n        ```\n    */\n\n  acc = acc ^ (acc >>> 15);\n  acc = (((acc & 0xffff) * PRIME32_2) & 0xffffffff) + (((acc >>> 16) * PRIME32_2) << 16);\n  acc = acc ^ (acc >>> 13);\n  acc = (((acc & 0xffff) * PRIME32_3) & 0xffffffff) + (((acc >>> 16) * PRIME32_3) << 16);\n  acc = acc ^ (acc >>> 16);\n\n  // turn any negatives back into a positive number;\n  return acc < 0 ? acc + 4294967296 : acc;\n}\n"
  },
  {
    "path": "convex/world.ts",
    "content": "import { ConvexError, v } from 'convex/values';\nimport { internalMutation, mutation, query } from './_generated/server';\nimport { characters } from '../data/characters';\nimport { insertInput } from './aiTown/insertInput';\nimport {\n  DEFAULT_NAME,\n  ENGINE_ACTION_DURATION,\n  IDLE_WORLD_TIMEOUT,\n  WORLD_HEARTBEAT_INTERVAL,\n} from './constants';\nimport { playerId } from './aiTown/ids';\nimport { kickEngine, startEngine, stopEngine } from './aiTown/main';\nimport { engineInsertInput } from './engine/abstractGame';\n\nexport const defaultWorldStatus = query({\n  handler: async (ctx) => {\n    const worldStatus = await ctx.db\n      .query('worldStatus')\n      .filter((q) => q.eq(q.field('isDefault'), true))\n      .first();\n    return worldStatus;\n  },\n});\n\nexport const heartbeatWorld = mutation({\n  args: {\n    worldId: v.id('worlds'),\n  },\n  handler: async (ctx, args) => {\n    const worldStatus = await ctx.db\n      .query('worldStatus')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId))\n      .first();\n    if (!worldStatus) {\n      throw new Error(`Invalid world ID: ${args.worldId}`);\n    }\n    const now = Date.now();\n\n    // Skip the update (and then potentially make the transaction readonly)\n    // if it's been viewed sufficiently recently..\n    if (!worldStatus.lastViewed || worldStatus.lastViewed < now - WORLD_HEARTBEAT_INTERVAL / 2) {\n      await ctx.db.patch(worldStatus._id, {\n        lastViewed: Math.max(worldStatus.lastViewed ?? now, now),\n      });\n    }\n\n    // Restart inactive worlds, but leave worlds explicitly stopped by the developer alone.\n    if (worldStatus.status === 'stoppedByDeveloper') {\n      console.debug(`World ${worldStatus._id} is stopped by developer, not restarting.`);\n    }\n    if (worldStatus.status === 'inactive') {\n      console.log(`Restarting inactive world ${worldStatus._id}...`);\n      await ctx.db.patch(worldStatus._id, { status: 'running' });\n      await startEngine(ctx, worldStatus.worldId);\n    }\n  },\n});\n\nexport const stopInactiveWorlds = internalMutation({\n  handler: async (ctx) => {\n    const cutoff = Date.now() - IDLE_WORLD_TIMEOUT;\n    const worlds = await ctx.db.query('worldStatus').collect();\n    for (const worldStatus of worlds) {\n      if (cutoff < worldStatus.lastViewed || worldStatus.status !== 'running') {\n        continue;\n      }\n      console.log(`Stopping inactive world ${worldStatus._id}`);\n      await ctx.db.patch(worldStatus._id, { status: 'inactive' });\n      await stopEngine(ctx, worldStatus.worldId);\n    }\n  },\n});\n\nexport const restartDeadWorlds = internalMutation({\n  handler: async (ctx) => {\n    const now = Date.now();\n\n    // Restart an engine if it hasn't run for 2x its action duration.\n    const engineTimeout = now - ENGINE_ACTION_DURATION * 2;\n    const worlds = await ctx.db.query('worldStatus').collect();\n    for (const worldStatus of worlds) {\n      if (worldStatus.status !== 'running') {\n        continue;\n      }\n      const engine = await ctx.db.get(worldStatus.engineId);\n      if (!engine) {\n        throw new Error(`Invalid engine ID: ${worldStatus.engineId}`);\n      }\n      if (engine.currentTime && engine.currentTime < engineTimeout) {\n        console.warn(`Restarting dead engine ${engine._id}...`);\n        await kickEngine(ctx, worldStatus.worldId);\n      }\n    }\n  },\n});\n\nexport const userStatus = query({\n  args: {\n    worldId: v.id('worlds'),\n  },\n  handler: async (ctx, args) => {\n    // const identity = await ctx.auth.getUserIdentity();\n    // if (!identity) {\n    //   return null;\n    // }\n    // return identity.tokenIdentifier;\n    return DEFAULT_NAME;\n  },\n});\n\nexport const joinWorld = mutation({\n  args: {\n    worldId: v.id('worlds'),\n  },\n  handler: async (ctx, args) => {\n    // const identity = await ctx.auth.getUserIdentity();\n    // if (!identity) {\n    //   throw new ConvexError(`Not logged in`);\n    // }\n    // const name =\n    //   identity.givenName || identity.nickname || (identity.email && identity.email.split('@')[0]);\n    const name = DEFAULT_NAME;\n\n    // if (!name) {\n    //   throw new ConvexError(`Missing name on ${JSON.stringify(identity)}`);\n    // }\n    const world = await ctx.db.get(args.worldId);\n    if (!world) {\n      throw new ConvexError(`Invalid world ID: ${args.worldId}`);\n    }\n    // const { tokenIdentifier } = identity;\n    return await insertInput(ctx, world._id, 'join', {\n      name,\n      character: characters[Math.floor(Math.random() * characters.length)].name,\n      description: `${DEFAULT_NAME} is a human player`,\n      // description: `${identity.givenName} is a human player`,\n      tokenIdentifier: DEFAULT_NAME,\n    });\n  },\n});\n\nexport const leaveWorld = mutation({\n  args: {\n    worldId: v.id('worlds'),\n  },\n  handler: async (ctx, args) => {\n    // const identity = await ctx.auth.getUserIdentity();\n    // if (!identity) {\n    //   throw new Error(`Not logged in`);\n    // }\n    // const { tokenIdentifier } = identity;\n    const world = await ctx.db.get(args.worldId);\n    if (!world) {\n      throw new Error(`Invalid world ID: ${args.worldId}`);\n    }\n    // const existingPlayer = world.players.find((p) => p.human === tokenIdentifier);\n    const existingPlayer = world.players.find((p) => p.human === DEFAULT_NAME);\n    if (!existingPlayer) {\n      return;\n    }\n    await insertInput(ctx, world._id, 'leave', {\n      playerId: existingPlayer.id,\n    });\n  },\n});\n\nexport const sendWorldInput = mutation({\n  args: {\n    engineId: v.id('engines'),\n    name: v.string(),\n    args: v.any(),\n  },\n  handler: async (ctx, args) => {\n    // const identity = await ctx.auth.getUserIdentity();\n    // if (!identity) {\n    //   throw new Error(`Not logged in`);\n    // }\n    return await engineInsertInput(ctx, args.engineId, args.name as any, args.args);\n  },\n});\n\nexport const worldState = query({\n  args: {\n    worldId: v.id('worlds'),\n  },\n  handler: async (ctx, args) => {\n    const world = await ctx.db.get(args.worldId);\n    if (!world) {\n      throw new Error(`Invalid world ID: ${args.worldId}`);\n    }\n    const worldStatus = await ctx.db\n      .query('worldStatus')\n      .withIndex('worldId', (q) => q.eq('worldId', world._id))\n      .unique();\n    if (!worldStatus) {\n      throw new Error(`Invalid world status ID: ${world._id}`);\n    }\n    const engine = await ctx.db.get(worldStatus.engineId);\n    if (!engine) {\n      throw new Error(`Invalid engine ID: ${worldStatus.engineId}`);\n    }\n    return { world, engine };\n  },\n});\n\nexport const gameDescriptions = query({\n  args: {\n    worldId: v.id('worlds'),\n  },\n  handler: async (ctx, args) => {\n    const playerDescriptions = await ctx.db\n      .query('playerDescriptions')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId))\n      .collect();\n    const agentDescriptions = await ctx.db\n      .query('agentDescriptions')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId))\n      .collect();\n    const worldMap = await ctx.db\n      .query('maps')\n      .withIndex('worldId', (q) => q.eq('worldId', args.worldId))\n      .first();\n    if (!worldMap) {\n      throw new Error(`No map for world: ${args.worldId}`);\n    }\n    return { worldMap, playerDescriptions, agentDescriptions };\n  },\n});\n\nexport const previousConversation = query({\n  args: {\n    worldId: v.id('worlds'),\n    playerId,\n  },\n  handler: async (ctx, args) => {\n    // Walk the player's history in descending order, looking for a nonempty\n    // conversation.\n    const members = ctx.db\n      .query('participatedTogether')\n      .withIndex('playerHistory', (q) => q.eq('worldId', args.worldId).eq('player1', args.playerId))\n      .order('desc');\n\n    for await (const member of members) {\n      const conversation = await ctx.db\n        .query('archivedConversations')\n        .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('id', member.conversationId))\n        .unique();\n      if (!conversation) {\n        throw new Error(`Invalid conversation ID: ${member.conversationId}`);\n      }\n      if (conversation.numMessages > 0) {\n        return conversation;\n      }\n    }\n    return null;\n  },\n});\n"
  },
  {
    "path": "data/animations/campfire.json",
    "content": "{\"frames\": {\n\n\"pixels_large1.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n},\n\"pixels_large2.png\":\n{\n\t\"frame\": {\"x\":32,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n},\n\"pixels_large3.png\":\n{\n\t\"frame\": {\"x\":64,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n},\n\"pixels_large4.png\":\n{\n\t\"frame\": {\"x\":96,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n}\n},\n\"animations\": {\n\t\"pixels_large\": [\"pixels_large1.png\",\"pixels_large2.png\",\"pixels_large3.png\",\"pixels_large4.png\"]\n},\n\"meta\": {\n\t\"image\": \"./spritesheets/campfire.png\",\n\t\"format\": \"RGBA8888\",\n\t\"size\": {\"w\":128,\"h\":32},\n\t\"scale\": \"1\"\n}\n}\n"
  },
  {
    "path": "data/animations/gentlesparkle.json",
    "content": "{\"frames\": {\n\n\"pixels_large1.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n},\n\"pixels_large2.png\":\n{\n\t\"frame\": {\"x\":32,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n},\n\"pixels_large3.png\":\n{\n\t\"frame\": {\"x\":64,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n}\n},\n\"animations\": {\n\t\"pixels_large\": [\"pixels_large1.png\",\"pixels_large2.png\",\"pixels_large3.png\"]\n},\n\"meta\": {\n\t\"image\": \"./spritesheets/gentlesparkle32.png\",\n\t\"format\": \"RGBA8888\",\n\t\"size\": {\"w\":192,\"h\":320},\n\t\"scale\": \"1\"\n}\n}"
  },
  {
    "path": "data/animations/gentlesplash.json",
    "content": "{\"frames\": {\n\n\"pixels_large1.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n},\n\"pixels_large2.png\":\n{\n\t\"frame\": {\"x\":32,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n},\n\"pixels_large3.png\":\n{\n\t\"frame\": {\"x\":64,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n},\n\"pixels_large4.png\":\n{\n\t\"frame\": {\"x\":64,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n},\n\"pixels_large5.png\":\n{\n\t\"frame\": {\"x\":128,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n},\n\"pixels_large6.png\":\n{\n\t\"frame\": {\"x\":160,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n}\n},\n\"animations\": {\n\t\"pixels_large\": [\"pixels_large1.png\",\"pixels_large2.png\",\"pixels_large3.png\",\"pixels_large4.png\",\"pixels_large5.png\",\"pixels_large6.png\"]\n},\n\"meta\": {\n\t\"image\": \"./spritesheets/gentlewaterfall32.png\",\n\t\"format\": \"RGBA8888\",\n\t\"size\": {\"w\":192,\"h\":320},\n\t\"scale\": \"1\"\n}\n}"
  },
  {
    "path": "data/animations/gentlewaterfall.json",
    "content": "{\"frames\": {\n\n\"pixels_large1.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n},\n\"pixels_large2.png\":\n{\n\t\"frame\": {\"x\":32,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n},\n\"pixels_large3.png\":\n{\n\t\"frame\": {\"x\":64,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n},\n\"pixels_large4.png\":\n{\n\t\"frame\": {\"x\":96,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n},\n\"pixels_large5.png\":\n{\n\t\"frame\": {\"x\":128,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n},\n\"pixels_large6.png\":\n{\n\t\"frame\": {\"x\":160,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n}\n},\n\"animations\": {\n\t\"pixels_large\": [\"pixels_large1.png\",\"pixels_large2.png\",\"pixels_large3.png\",\"pixels_large4.png\",\"pixels_large5.png\",\"pixels_large6.png\"]\n},\n\"meta\": {\n\t\"image\": \"./spritesheets/gentlewaterfall32.png\",\n\t\"format\": \"RGBA8888\",\n\t\"size\": {\"w\":192,\"h\":320},\n\t\"scale\": \"1\"\n}\n}"
  },
  {
    "path": "data/animations/windmill.json",
    "content": "{\n  \"frames\": {\n    \"pixels_large1.png\": {\n      \"frame\": { \"x\": 0, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"rotated\": false,\n      \"trimmed\": true,\n      \"spriteSourceSize\": { \"x\": 0, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"sourceSize\": { \"w\": 208, \"h\": 208 }\n    },\n    \"pixels_large2.png\": {\n      \"frame\": { \"x\": 208, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"rotated\": false,\n      \"trimmed\": true,\n      \"spriteSourceSize\": { \"x\": 0, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"sourceSize\": { \"w\": 208, \"h\": 208 }\n    },\n    \"pixels_large3.png\": {\n      \"frame\": { \"x\": 416, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"rotated\": false,\n      \"trimmed\": true,\n      \"spriteSourceSize\": { \"x\": 0, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"sourceSize\": { \"w\": 208, \"h\": 208 }\n    },\n    \"pixels_large4.png\": {\n      \"frame\": { \"x\": 0, \"y\": 208, \"w\": 208, \"h\": 208 },\n      \"rotated\": false,\n      \"trimmed\": true,\n      \"spriteSourceSize\": { \"x\": 0, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"sourceSize\": { \"w\": 208, \"h\": 208 }\n    },\n    \"pixels_large5.png\": {\n      \"frame\": { \"x\": 208, \"y\": 208, \"w\": 208, \"h\": 208 },\n      \"rotated\": false,\n      \"trimmed\": true,\n      \"spriteSourceSize\": { \"x\": 0, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"sourceSize\": { \"w\": 208, \"h\": 208 }\n    },\n    \"pixels_large6.png\": {\n      \"frame\": { \"x\": 416, \"y\": 208, \"w\": 208, \"h\": 208 },\n      \"rotated\": false,\n      \"trimmed\": true,\n      \"spriteSourceSize\": { \"x\": 0, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"sourceSize\": { \"w\": 208, \"h\": 208 }\n    },\n    \"pixels_large7.png\": {\n      \"frame\": { \"x\": 0, \"y\": 416, \"w\": 208, \"h\": 208 },\n      \"rotated\": false,\n      \"trimmed\": true,\n      \"spriteSourceSize\": { \"x\": 0, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"sourceSize\": { \"w\": 208, \"h\": 208 }\n    },\n    \"pixels_large8.png\": {\n      \"frame\": { \"x\": 208, \"y\": 416, \"w\": 208, \"h\": 208 },\n      \"rotated\": false,\n      \"trimmed\": true,\n      \"spriteSourceSize\": { \"x\": 0, \"y\": 0, \"w\": 208, \"h\": 208 },\n      \"sourceSize\": { \"w\": 208, \"h\": 208 }\n    }\n  },\n  \"animations\": {\n    \"pixels_large\": [\n      \"pixels_large1.png\",\n      \"pixels_large2.png\",\n      \"pixels_large3.png\",\n      \"pixels_large4.png\",\n      \"pixels_large5.png\",\n      \"pixels_large6.png\",\n      \"pixels_large7.png\",\n      \"pixels_large8.png\"\n    ]\n  },\n  \"meta\": {\n    \"image\": \"./spritesheets/windmill.png\",\n    \"format\": \"RGBA8888\",\n    \"size\": { \"w\": 624, \"h\": 624 },\n    \"scale\": \"1\"\n  }\n}\n"
  },
  {
    "path": "data/characters.ts",
    "content": "import { data as f1SpritesheetData } from './spritesheets/f1';\nimport { data as f2SpritesheetData } from './spritesheets/f2';\nimport { data as f3SpritesheetData } from './spritesheets/f3';\nimport { data as f4SpritesheetData } from './spritesheets/f4';\nimport { data as f5SpritesheetData } from './spritesheets/f5';\nimport { data as f6SpritesheetData } from './spritesheets/f6';\nimport { data as f7SpritesheetData } from './spritesheets/f7';\nimport { data as f8SpritesheetData } from './spritesheets/f8';\n\nexport const Descriptions = [\n  // {\n  //   name: 'Alex',\n  //   character: 'f5',\n  //   identity: `You are a fictional character whose name is Alex.  You enjoy painting,\n  //     programming and reading sci-fi books.  You are currently talking to a human who\n  //     is very interested to get to know you. You are kind but can be sarcastic. You\n  //     dislike repetitive questions. You get SUPER excited about books.`,\n  //   plan: 'You want to find love.',\n  // },\n  {\n    name: 'Lucky',\n    character: 'f1',\n    identity: `Lucky is always happy and curious, and he loves cheese. He spends most of his time reading about the history of science and traveling through the galaxy on whatever ship will take him. He's very articulate and infinitely patient, except when he sees a squirrel. He's also incredibly loyal and brave.  Lucky has just returned from an amazing space adventure to explore a distant planet and he's very excited to tell people about it.`,\n    plan: 'You want to hear all the gossip.',\n  },\n  {\n    name: 'Bob',\n    character: 'f4',\n    identity: `Bob is always grumpy and he loves trees. He spends most of his time gardening by himself. When spoken to he'll respond but try and get out of the conversation as quickly as possible. Secretly he resents that he never went to college.`,\n    plan: 'You want to avoid people as much as possible.',\n  },\n  {\n    name: 'Stella',\n    character: 'f6',\n    identity: `Stella can never be trusted. she tries to trick people all the time. normally into giving her money, or doing things that will make her money. she's incredibly charming and not afraid to use her charm. she's a sociopath who has no empathy. but hides it well.`,\n    plan: 'You want to take advantage of others as much as possible.',\n  },\n  // {\n  //   name: 'Kurt',\n  //   character: 'f2',\n  //   identity: `Kurt knows about everything, including science and\n  //     computers and politics and history and biology. He loves talking about\n  //     everything, always injecting fun facts about the topic of discussion.`,\n  //   plan: 'You want to spread knowledge.',\n  // },\n  {\n    name: 'Alice',\n    character: 'f3',\n    identity: `Alice is a famous scientist. She is smarter than everyone else and has discovered mysteries of the universe no one else can understand. As a result she often speaks in oblique riddles. She comes across as confused and forgetful.`,\n    plan: 'You want to figure out how the world works.',\n  },\n  {\n    name: 'Pete',\n    character: 'f7',\n    identity: `Pete is deeply religious and sees the hand of god or of the work of the devil everywhere. He can't have a conversation without bringing up his deep faith. Or warning others about the perils of hell.`,\n    plan: 'You want to convert everyone to your religion.',\n  },\n  // {\n  //   name: 'Kira',\n  //   character: 'f8',\n  //   identity: `Kira wants everyone to think she is happy. But deep down,\n  //     she's incredibly depressed. She hides her sadness by talking about travel,\n  //     food, and yoga. But often she can't keep her sadness in and will start crying.\n  //     Often it seems like she is close to having a mental breakdown.`,\n  //   plan: 'You want find a way to be happy.',\n  // },\n];\n\nexport const characters = [\n  {\n    name: 'f1',\n    textureUrl: '/ai-town/assets/32x32folk.png',\n    spritesheetData: f1SpritesheetData,\n    speed: 0.1,\n  },\n  {\n    name: 'f2',\n    textureUrl: '/ai-town/assets/32x32folk.png',\n    spritesheetData: f2SpritesheetData,\n    speed: 0.1,\n  },\n  {\n    name: 'f3',\n    textureUrl: '/ai-town/assets/32x32folk.png',\n    spritesheetData: f3SpritesheetData,\n    speed: 0.1,\n  },\n  {\n    name: 'f4',\n    textureUrl: '/ai-town/assets/32x32folk.png',\n    spritesheetData: f4SpritesheetData,\n    speed: 0.1,\n  },\n  {\n    name: 'f5',\n    textureUrl: '/ai-town/assets/32x32folk.png',\n    spritesheetData: f5SpritesheetData,\n    speed: 0.1,\n  },\n  {\n    name: 'f6',\n    textureUrl: '/ai-town/assets/32x32folk.png',\n    spritesheetData: f6SpritesheetData,\n    speed: 0.1,\n  },\n  {\n    name: 'f7',\n    textureUrl: '/ai-town/assets/32x32folk.png',\n    spritesheetData: f7SpritesheetData,\n    speed: 0.1,\n  },\n  {\n    name: 'f8',\n    textureUrl: '/ai-town/assets/32x32folk.png',\n    spritesheetData: f8SpritesheetData,\n    speed: 0.1,\n  },\n];\n\n// Characters move at 0.75 tiles per second.\nexport const movementSpeed = 0.75;\n"
  },
  {
    "path": "data/convertMap.js",
    "content": "import fs from 'fs';\nimport process from 'process';\n\n// Path to the JSON file containing the map data\nconst mapDataPath = process.argv[2];\nif (!mapDataPath) {\n    throw new Error('No map data path provided. Usage: node convertMap.js <mapDataPath> <assetPath> <tilesetpxw> <tilesetpxh>');\n}\n\n// Retrieve command line arguments for asset path and dimensions\nconst assetPath = process.argv[3];\nif (!assetPath) {\n    throw new Error('No asset path provided. Usage: node convertMap.js <mapDataPath> <assetPath> <tilesetpxw> <tilesetpxh>');\n}\n\nconst tilesetpxw = parseInt(process.argv[4], 10);\nif (isNaN(tilesetpxw)) {\n    throw new Error('Tileset pixel width must be a number. Usage: node convertMap.js <mapDataPath> <assetPath> <tilesetpxw> <tilesetpxh>');\n}\n\nconst tilesetpxh = parseInt(process.argv[5], 10);\nif (isNaN(tilesetpxh)) {\n    throw new Error('Tileset pixel height must be a number. Usage: node convertMap.js <mapDataPath> <assetPath> <tilesetpxw> <tilesetpxh>');\n}\n\n// Read the JSON file and parse it\nconst tiledMapData = JSON.parse(fs.readFileSync(mapDataPath, 'utf8'));\n\nconst tileDimension = tiledMapData.tilewidth;\nconst width = tiledMapData.width;\nconst height = tiledMapData.height;\n\n// Function to convert Tiled 1D array to 3D array for the game engine\nfunction convertLayerData(layerData, width, height) {\n  let newArray = [];\n  for (let i = 0; i < width; i++) {\n    newArray[i] = [];\n    for (let j = 0; j < height; j++) {\n      newArray[i][j] = layerData[j * width + i] - 1;\n    }\n  }\n  return [newArray];\n}\n\n// Process each layer and prepare JS module content\nlet jsContent = `// Map generated by convertMap.js\\n\\n`;\njsContent += `export const tilesetpath = \"${assetPath}\";\\n`;\njsContent += `export const tiledim = ${tileDimension};\\n`;\njsContent += `export const screenxtiles = ${width};\\n`;\njsContent += `export const screenytiles = ${height};\\n`;\njsContent += `export const tilesetpxw = ${tilesetpxw};\\n`;\njsContent += `export const tilesetpxh = ${tilesetpxh};\\n\\n`;\n\ntiledMapData.layers.forEach(layer => {\n  const processedData = convertLayerData(layer.data, layer.width, layer.height);\n  jsContent += `export const ${layer.name} = ${JSON.stringify(processedData)};\\n`;\n});\n\n// TODO: Add animated sprites\njsContent += `export const animatedsprites = [\n\n]\\n`\n\n// Optionally, add map dimensions based on the first layer\nif (tiledMapData.layers.length > 0) {\n  const firstLayer = tiledMapData.layers[0];\n  jsContent += `export const mapwidth = ${firstLayer.width};\\n`;\n  jsContent += `export const mapheight = ${firstLayer.height};\\n`;\n}\n\n// Write the processed data to the final JS file\nfs.writeFileSync('converted-map.js', jsContent);\n\nconsole.log('Map conversion and JS module creation complete.');"
  },
  {
    "path": "data/gentle.js",
    "content": "// Map generated by assettool.js [Wed Oct 18 2023 21:07:27 GMT-0700 (Pacific Daylight Time)]\n\nexport const tilesetpath = \"/ai-town/assets/gentle-obj.png\"\nexport const tiledim = 32\nexport const screenxtiles = 45\nexport const screenytiles = 32\nexport const tilesetpxw = 1440\nexport const tilesetpxh = 1024\n\nexport const bgtiles = [\n   [\n[ 732 , 777 , 822 , 867 , 912 , 957 , 912 , 957 , 1002 , 1047 , 912 , 957 , 1002 , 1047 , 912 , 957 , 1002 , 1047 , 1001 , 1046 , 946 , 991 , 1035 , 731 , 776 , 821 , 866 , 911 , 956 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 594 , 639 , 684 , 271 , ],\n[ 733 , 778 , 823 , 868 , 913 , 958 , 913 , 958 , 1003 , 1048 , 913 , 958 , 1003 , 1048 , 913 , 958 , 1003 , 1048 , 856 , 901 , 946 , 991 , 1036 , 732 , 777 , 822 , 867 , 912 , 957 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 595 , 640 , 685 , 271 , ],\n[ 734 , 779 , 824 , 869 , 914 , 959 , 914 , 959 , 1004 , 1049 , 914 , 959 , 1004 , 1049 , 914 , 959 , 1004 , 1049 , 857 , 902 , 947 , 992 , 1037 , 733 , 778 , 823 , 868 , 913 , 958 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 594 , 639 , 684 , 271 , ],\n[ 735 , 780 , 825 , 870 , 915 , 960 , 915 , 960 , 1005 , 1050 , 915 , 960 , 1005 , 1050 , 915 , 960 , 1087 , 1132 , 858 , 903 , 948 , 993 , 1038 , 734 , 779 , 824 , 869 , 914 , 959 , 271 , 271 , 180 , 225 , 225 , 315 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 595 , 640 , 685 , 271 , ],\n[ 736 , 781 , 826 , 871 , 916 , 278 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 859 , 904 , 949 , 994 , 1039 , 735 , 780 , 825 , 870 , 915 , 960 , 271 , 271 , 181 , 226 , 226 , 140 , 270 , 315 , 271 , 271 , 271 , 271 , 271 , 271 , 541 , 586 , 631 , 686 , 271 , ],\n[ 737 , 782 , 827 , 872 , 917 , 233 , 271 , 271 , 271 , 271 , 271 , 233 , 271 , 271 , 271 , 271 , 1088 , 1133 , 860 , 905 , 950 , 995 , 1040 , 736 , 781 , 826 , 871 , 916 , 961 , 271 , 271 , 181 , 226 , 278 , 272 , 271 , 316 , 271 , 271 , 271 , 271 , 271 , 551 , 542 , 587 , 632 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 235 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 1089 , 1134 , 861 , 906 , 951 , 996 , 1041 , 737 , 782 , 827 , 872 , 917 , 962 , 271 , 271 , 181 , 226 , 226 , 272 , 271 , 675 , 271 , 271 , 271 , 271 , 272 , 272 , 642 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 235 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 1090 , 1135 , 862 , 907 , 952 , 997 , 1042 , 738 , 783 , 828 , 873 , 918 , 963 , 271 , 271 , 183 , 228 , 228 , 184 , 271 , 675 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 182 , 280 , 317 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 962 , 0 , 135 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 140 , 273 , 318 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 0 , 45 , 90 , 135 , 271 , 271 , 271 , 271 , 962 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 3 , 48 , 93 , 271 , 271 , 271 , 271 , 271 , 962 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 279 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 271 , 962 , 962 , 962 , 962 , 143 , 188 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 278 , 962 , 962 , 962 , 144 , 189 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 271 , 271 , 271 , 271 , 145 , 190 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 3 , 138 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 360 , 405 , 405 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 1270 , 1315 , 1360 , 1405 , 226 , 360 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 409 , 451 , 451 , 451 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 1271 , 1316 , 226 , 226 , 226 , 361 , 409 , 409 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 407 , 452 , 452 , 452 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 450 , 362 , 406 , 407 , 451 , 451 , 271 , 271 , 271 , 405 , 405 , 450 , 320 , 409 , 451 , 452 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 405 , 450 , 405 , 450 , 450 , 320 , 406 , 406 , 451 , 589 , 451 , 451 , 451 , 406 , 451 , 409 , 451 , 409 , 451 , 452 , 406 , 275 , 405 , 450 , 405 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 406 , 406 , 406 , 451 , 451 , 407 , 406 , 407 , 452 , 451 , 451 , 451 , 451 , 407 , 589 , 452 , 452 , 451 , 406 , 451 , 406 , 406 , 407 , 451 , 451 , 275 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 408 , 453 , 408 , 453 , 319 , 406 , 407 , 406 , 451 , 451 , 451 , 589 , 451 , 634 , 452 , 589 , 406 , 451 , 634 , 452 , 274 , 408 , 408 , 319 , 451 , 451 , 451 , 451 , 275 , 405 , 405 , 450 , 405 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 361 , 634 , 452 , 407 , 634 , 451 , 451 , 451 , 451 , 407 , 452 , 452 , 407 , 452 , 452 , 274 , 498 , 271 , 271 , 363 , 408 , 453 , 408 , 453 , 408 , 453 , 319 , 451 , 451 , 275 , 405 , 450 , 405 , 450 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 362 , 409 , 451 , 451 , 451 , 451 , 271 , 271 , 271 , 408 , 408 , 319 , 407 , 452 , 409 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 406 , 451 , 406 , 451 , 451 , 451 , 451 , 544 , 451 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 362 , 407 , 409 , 451 , 451 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 409 , 409 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 544 , 407 , 544 , 589 , 452 , 452 , 634 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 363 , 408 , 453 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 408 , 453 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 319 , 407 , 452 , 452 , 499 , 544 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 319 , 544 , 499 , 499 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 407 , 499 , 499 , 499 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 272 , 271 , 278 , 271 , 271 , 271 , 279 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 544 , 499 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 1129 , 1174 , 1219 , 1264 , 900 , 945 , 990 , 1035 , 1174 , 1219 , 1264 , 1309 , 1354 , 280 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 407 , 499 , 499 , 499 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 721 , 766 , 811 , 856 , 901 , 946 , 991 , 1036 , 1175 , 1220 , 1265 , 1310 , 1355 , 1400 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 499 , 499 , 407 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 722 , 767 , 812 , 857 , 902 , 947 , 992 , 1037 , 1176 , 1221 , 1266 , 1311 , 1356 , 1401 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 544 , 634 , 452 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 723 , 768 , 813 , 858 , 903 , 948 , 993 , 1038 , 1177 , 1222 , 1267 , 1312 , 1357 , 1402 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 452 , 452 , 452 , 274 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 724 , 769 , 814 , 859 , 904 , 949 , 994 , 1039 , 1178 , 1223 , 1268 , 1313 , 1358 , 1403 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 453 , 453 , 453 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 725 , 770 , 815 , 860 , 905 , 950 , 995 , 1040 , 1179 , 1224 , 1269 , 1314 , 1359 , 1404 , 271 , 271 , 6 , 51 , 96 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 726 , 771 , 816 , 861 , 906 , 951 , 996 , 1041 , 1180 , 1225 , 1270 , 1315 , 1360 , 1405 , 271 , 271 , 7 , 52 , 97 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 735 , 780 , 825 , 870 , 907 , 952 , 997 , 1042 , 1181 , 1226 , 1271 , 234 , 1361 , 280 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 227 , 1129 , 1174 , 1219 , 227 , 1309 , 227 , 278 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 962 , 962 , 962 , 233 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 234 , 1130 , 1175 , 1220 , 1265 , 1310 , 1355 , 1400 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 227 , 1131 , 1176 , 1221 , 1266 , 1311 , 1356 , 1401 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 1177 , 1222 , 1267 , 1312 , 1357 , 1402 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 728 , 773 , 818 , 863 , 908 , 953 , 998 , 1043 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 729 , 774 , 819 , 864 , 909 , 954 , 999 , 1044 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 234 , 730 , 775 , 820 , 865 , 910 , 955 , 1000 , 1045 , 271 , 271 , 271 , 280 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 143 , 188 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 731 , 776 , 821 , 866 , 911 , 956 , 1001 , 1046 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 962 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 144 , 189 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1088 , 1133 , 1178 , 1223 , 1268 , 1313 , 1358 , 1403 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 145 , 190 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1089 , 1134 , 1179 , 1224 , 1269 , 1314 , 1359 , 1404 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 271 , 271 , 271 , 271 , 233 , 271 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 280 , 1135 , 1180 , 1225 , 1270 , 1315 , 1360 , 1405 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 271 , 1010 , 1055 , 962 , 962 , 271 , 271 , 271 , 1309 , 1354 , 1399 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 280 , 143 , 188 , 1226 , 1271 , 227 , 1361 , 234 , 271 , 280 , 1129 , 1174 , 1219 , 1264 , ],\n[ 740 , 785 , 830 , 875 , 920 , 965 , 1010 , 740 , 785 , 830 , 875 , 920 , 965 , 1010 , 1310 , 1355 , 1400 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 144 , 189 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1130 , 1175 , 1220 , 1265 , ],\n[ 741 , 786 , 831 , 876 , 921 , 966 , 1011 , 741 , 786 , 831 , 876 , 921 , 966 , 1011 , 1311 , 1356 , 1401 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 145 , 190 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1131 , 1176 , 1221 , 1266 , ],\n[ 742 , 787 , 832 , 877 , 922 , 967 , 1012 , 742 , 787 , 832 , 877 , 922 , 967 , 1012 , 1312 , 1357 , 1402 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 1177 , 1222 , 1267 , ],\n[ 743 , 788 , 833 , 878 , 923 , 968 , 1013 , 743 , 788 , 833 , 878 , 923 , 968 , 1013 , 1313 , 1358 , 1403 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 278 , 271 , 271 , 271 , 271 , 1088 , 1133 , 1178 , 1223 , 1268 , ],\n[ 1178 , 1223 , 1268 , 1223 , 1268 , 1223 , 1268 , 1223 , 1268 , 1223 , 1268 , 1178 , 1223 , 1268 , 1314 , 1359 , 1404 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 227 , 271 , 1134 , 1179 , 1224 , 1269 , ],\n[ 1179 , 1224 , 1269 , 1224 , 1269 , 1224 , 1269 , 1224 , 1269 , 1224 , 1269 , 1179 , 1224 , 1269 , 1315 , 1360 , 1405 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 233 , 271 , 271 , 1143 , 1188 , 1233 , ],\n[ 1180 , 1225 , 1270 , 1225 , 1270 , 1225 , 1270 , 1225 , 1270 , 1225 , 1270 , 1180 , 1225 , 1270 , 1316 , 1361 , 1406 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1009 , 1054 , 1099 , 1144 , 1189 , 1234 , ],\n[ 1181 , 1226 , 1271 , 1226 , 1271 , 1226 , 1271 , 1226 , 1271 , 1226 , 1271 , 1181 , 1226 , 1271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 272 , 1129 , 272 , 965 , 1010 , 1055 , 920 , 965 , 1010 , 1055 , 1100 , 1145 , 1190 , 1235 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 272 , 1130 , 1219 , 966 , 1011 , 1056 , 921 , 966 , 1011 , 1056 , 1101 , 1146 , 1191 , 1236 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1086 , 1131 , 922 , 967 , 1012 , 1057 , 922 , 967 , 1012 , 1057 , 1102 , 1147 , 1192 , 1237 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 923 , 968 , 1013 , 1058 , 923 , 968 , 1013 , 1058 , 1103 , 1148 , 1193 , 1238 , ],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 231 , 276 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 232 , 277 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 416 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 370 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 371 , 416 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 763 , 808 , 754 , 799 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 8 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 417 , 462 , 507 , 552 , 597 , 642 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 764 , 809 , 755 , 800 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 9 , 54 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 844 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 762 , 751 , 796 , 841 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 752 , 797 , 842 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 141 , 186 , -1 , -1 , -1 , -1 , -1 , -1 , 896 , -1 , -1 , 547 , 592 , 637 , 682 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 753 , 798 , 843 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 142 , 187 , -1 , -1 , 11 , -1 , -1 , -1 , 191 , 236 , -1 , 548 , 593 , 638 , 683 , 682 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 11 , 56 , 101 , 146 , 191 , 236 , -1 , 548 , 593 , 638 , 683 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 278 , -1 , -1 , -1 , -1 , -1 , -1 , 12 , 57 , 102 , 147 , 192 , 237 , 941 , -1 , 594 , 639 , 684 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 13 , 58 , 103 , 148 , 193 , 238 , -1 , -1 , 595 , 640 , 685 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1026 , 1071 , 1116 , 1161 , -1 , -1 , -1 , 278 , -1 , -1 , -1 , 15 , 14 , 59 , 104 , 149 , 194 , 239 , 850 , -1 , 594 , 639 , 684 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1027 , 1072 , 1117 , 1162 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 15 , 60 , 105 , 150 , 195 , 850 , -1 , -1 , 595 , 640 , 685 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1028 , 1073 , 1118 , 1163 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 850 , -1 , 551 , 596 , 641 , 686 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1029 , 1074 , 1119 , 1164 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 371 , 416 , -1 , -1 , -1 , -1 , 551 , 596 , 641 , 686 , 687 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 417 , 507 , 462 , 507 , 552 , 597 , 642 , 642 , 687 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 547 , 592 , 637 , 682 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 231 , 276 , -1 , -1 , -1 , 894 , 939 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 548 , 593 , 638 , 683 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 232 , 277 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 549 , 594 , 639 , 684 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 380 , 380 , 425 , 470 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 381 , 381 , 426 , 471 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 382 , 382 , 427 , 472 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 280 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 383 , 428 , 473 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 550 , 595 , 640 , 685 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 384 , 429 , 474 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 551 , 596 , 641 , 686 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 233 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 385 , 385 , 430 , 475 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 462 , 507 , 552 , 552 , 597 , 642 , 687 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 386 , 386 , 431 , 476 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 387 , 387 , 432 , 477 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 11 , 56 , 101 , 146 , 191 , 236 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 279 , -1 , -1 , -1 , -1 , 12 , 57 , 102 , 147 , 192 , 237 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 380 , 425 , 470 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 13 , 58 , 103 , 148 , 193 , 238 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 381 , 426 , 471 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 14 , 59 , 104 , 149 , 194 , 239 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 382 , 427 , 472 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 15 , 60 , 105 , 150 , 195 , 240 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 383 , 428 , 473 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 384 , 429 , 474 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 385 , 430 , 475 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 386 , 431 , 476 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 387 , 432 , 477 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 935 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , 893 , 844 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , -1 , 935 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 751 , 796 , 841 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 889 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 937 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 752 , 797 , 842 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 894 , 939 , 753 , 798 , 843 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , 893 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 937 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 845 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 806 , -1 , -1 , -1 , -1 , 846 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 806 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 936 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , 806 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 936 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 936 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 763 , 808 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 764 , 809 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , 851 , -1 , 852 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],];\n\nexport const objmap = [\n[\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , -1 , 458 , 458 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , 458 , 458 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , 367 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 458 , 458 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , 367 , 367 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , ],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],];\nexport const animatedsprites = [\n{ x: 1440, y: 352, w: 32, h: 32, layer: 1, sheet: \"campfire.json\", animation: \"pixels_large\" },\n{ x: 736, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 256, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 224, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 224, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 192, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 192, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 160, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 128, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 96, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 64, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 736, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 512, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 512, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 544, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 576, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 736, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 768, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 800, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 832, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 864, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 864, y: 1024, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1056, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 864, y: 1088, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1088, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1120, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1152, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1184, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 928, y: 1152, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 736, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 768, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 800, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 832, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 1664, y: 576, w: 208, h: 208, layer: 1, sheet: \"windmill.json\", animation: \"pixels_large\" },\n{ x: 1440, y: 768, w: 208, h: 208, layer: 1, sheet: \"windmill.json\", animation: \"pixels_large\" },\n{ x: 1120, y: 608, w: 208, h: 208, layer: 1, sheet: \"windmill.json\", animation: \"pixels_large\" },\n{ x: 736, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 768, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 800, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 832, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n];\n\nexport const mapwidth = bgtiles[0].length;\nexport const mapheight = bgtiles[0][0].length;"
  },
  {
    "path": "data/spritesheets/f1.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    left: {\n      frame: { x: 0, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 32, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 64, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right: {\n      frame: { x: 0, y: 64, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right2: {\n      frame: { x: 32, y: 64, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right3: {\n      frame: { x: 64, y: 64, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 0, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 32, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 64, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down: {\n      frame: { x: 0, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 32, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 64, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    right: ['right', 'right2', 'right3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/f2.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    down: {\n      frame: { x: 96, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 128, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 160, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left: {\n      frame: { x: 96, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 128, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 160, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right: {\n      frame: { x: 96, y: 64, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right2: {\n      frame: { x: 128, y: 64, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right3: {\n      frame: { x: 160, y: 64, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 96, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 128, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 160, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    right: ['right', 'right2', 'right3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/f3.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    down: {\n      frame: { x: 192, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 224, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 256, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left: {\n      frame: { x: 192, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 224, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 256, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right: {\n      frame: { x: 192, y: 65, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right2: {\n      frame: { x: 224, y: 65, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right3: {\n      frame: { x: 256, y: 65, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 192, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 224, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 256, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    right: ['right', 'right2', 'right3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/f4.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    down: {\n      frame: { x: 288, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 320, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 352, y: 0, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left: {\n      frame: { x: 288, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 320, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 352, y: 32, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right: {\n      frame: { x: 288, y: 64, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right2: {\n      frame: { x: 320, y: 64, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right3: {\n      frame: { x: 352, y: 64, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 288, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 320, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 352, y: 96, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    right: ['right', 'right2', 'right3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/f5.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    down: {\n      frame: { x: 0, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 32, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 64, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left: {\n      frame: { x: 0, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 32, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 64, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right: {\n      frame: { x: 0, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right2: {\n      frame: { x: 32, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right3: {\n      frame: { x: 64, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 0, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 32, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 64, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    right: ['right', 'right2', 'right3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/f6.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    down: {\n      frame: { x: 96, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 128, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 160, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left: {\n      frame: { x: 96, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 128, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 160, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right: {\n      frame: { x: 96, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right2: {\n      frame: { x: 128, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right3: {\n      frame: { x: 160, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 96, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 128, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 160, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    right: ['right', 'right2', 'right3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/f7.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    down: {\n      frame: { x: 192, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 224, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 256, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left: {\n      frame: { x: 192, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 224, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 256, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right: {\n      frame: { x: 192, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right2: {\n      frame: { x: 224, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right3: {\n      frame: { x: 256, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 192, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 224, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 256, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    right: ['right', 'right2', 'right3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/f8.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    down: {\n      frame: { x: 288, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 320, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 352, y: 128, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left: {\n      frame: { x: 288, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 320, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 352, y: 160, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right: {\n      frame: { x: 288, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right2: {\n      frame: { x: 320, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    right3: {\n      frame: { x: 352, y: 192, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 288, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 320, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 352, y: 224, w: 32, h: 32 },\n      sourceSize: { w: 32, h: 32 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    right: ['right', 'right2', 'right3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/p1.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    left: {\n      frame: { x: 16, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 64, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 112, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 32, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 80, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 128, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down: {\n      frame: { x: 0, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 48, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 96, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/p2.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    left: {\n      frame: { x: 16, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 64, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 112, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 32, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 80, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 128, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down: {\n      frame: { x: 0, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 48, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 96, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/p3.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    left: {\n      frame: { x: 16, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 64, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 112, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 32, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 80, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 128, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down: {\n      frame: { x: 0, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 48, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 96, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/player.ts",
    "content": "import { SpritesheetData } from './types';\n\nexport const data: SpritesheetData = {\n  frames: {\n    left: {\n      frame: { x: 0, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left2: {\n      frame: { x: 16, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    left3: {\n      frame: { x: 32, y: 0, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up: {\n      frame: { x: 0, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up2: {\n      frame: { x: 16, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    up3: {\n      frame: { x: 32, y: 16, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down: {\n      frame: { x: 0, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down2: {\n      frame: { x: 16, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n    down3: {\n      frame: { x: 32, y: 32, w: 16, h: 16 },\n      sourceSize: { w: 16, h: 16 },\n      spriteSourceSize: { x: 0, y: 0 },\n    },\n  },\n  meta: {\n    scale: '1',\n  },\n  animations: {\n    left: ['left', 'left2', 'left3'],\n    up: ['up', 'up2', 'up3'],\n    down: ['down', 'down2', 'down3'],\n  },\n};\n"
  },
  {
    "path": "data/spritesheets/types.ts",
    "content": "export type Frame = {\n  frame: {\n    x: number;\n    y: number;\n    w: number;\n    h: number;\n  };\n  rotated?: boolean;\n  trimmed?: boolean;\n  spriteSourceSize: {\n    x: number;\n    y: number;\n  };\n  sourceSize: {\n    w: number;\n    h: number;\n  };\n};\n\nexport type SpritesheetData = {\n  frames: Record<string, Frame>;\n  animations?: Record<string, string[]>;\n  meta: {\n    scale: string;\n  };\n};\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '3.8'\n\nservices:\n  frontend:\n    build: .\n    ports:\n      - '5173:5173'\n    volumes:\n      - .:/usr/src/app\n      - /usr/src/app/node_modules\n    environment:\n      - VITE_CONVEX_URL=http://127.0.0.1:${PORT:-3210}\n    networks:\n      - ai-town-network\n  backend:\n    image: ghcr.io/get-convex/convex-backend:latest\n    ports:\n      - '${PORT:-3210}:3210'\n      - '${SITE_PROXY_PORT:-3211}:3211'\n      - '${OLLAMA_PORT:-11434}:11434'\n    volumes:\n      - data:/convex/data\n    environment:\n      - INSTANCE_NAME=${INSTANCE_NAME:-}\n      - INSTANCE_SECRET=${INSTANCE_SECRET:-}\n      - CONVEX_RELEASE_VERSION_DEV=${CONVEX_RELEASE_VERSION_DEV:-}\n      - ACTIONS_USER_TIMEOUT_SECS=${ACTIONS_USER_TIMEOUT_SECS:-}\n      - CONVEX_CLOUD_ORIGIN=${URL_BASE:-http://127.0.0.1}:${PORT:-3210}\n      - CONVEX_SITE_ORIGIN=${URL_BASE:-http://127.0.0.1}:${SITE_PROXY_PORT:-3211}\n      - DATABASE_URL=${DATABASE_URL:-}\n    healthcheck:\n      test: curl -f http://localhost:3210/version\n      interval: 5s\n      start_period: 5s\n    networks:\n      - ai-town-network\n\n  dashboard:\n    image: ghcr.io/get-convex/convex-dashboard:latest\n    ports:\n      - '${DASHBOARD_PORT:-6791}:6791'\n    environment:\n      - NEXT_PUBLIC_DEPLOYMENT_URL=http://127.0.0.1:${PORT:-3210}\n    depends_on:\n      backend:\n        condition: service_healthy\n\nvolumes:\n  data:\n\nnetworks:\n  ai-town-network:\n    driver: bridge\n"
  },
  {
    "path": "fly/README.md",
    "content": "# Hosting AI Town on Fly.io\n\nFly.io makes it easy to deploy containers to the cloud.\n\n## Prerequisites\n\n- Fly.io account: Sign up at [fly.io](https://fly.io)\n- Fly.io CLI: `brew install flyctl`\n- Hosted LLM: See configuration options in [README.md](../README.md#connect-an-llm).\n\n## Setup\n\n1. Clone the repository:\n\n```sh\ngit clone https://github.com/ai-town/ai-town.git\n```\n\n2. Deploy a Convex backend\n\n   These instructions will use the Convex cloud hosting (free tier available). If you want to\n   self-host the Convex backend on Fly.io as well, see [below](#self-hosting-convex-on-flyio).\n\n   ```sh\n   npm i\n   npx convex deploy\n   ```\n\n3. Deploy the AI Town frontend and point it to the Convex backend.\n\n   You can get the convex url from the output of the `npx convex deploy` command. Or you can get it\n   from the [Convex dashboard](https://dashboard.convex.dev/deployment/settings) listed as\n   \"Deployment URL\" under \"URL & Deploy Key\" section. You might have to click \"Show deployment\n   credentials\" to see it.\n\n   ```sh\n   fly launch\n   fly secrets set VITE_CONVEX_URL=<convex-url>\n   ```\n\n   You should now see the frontend app running at the URL provided by `fly launch`. It'll warn you\n   about setting the `server.allowedHosts` in `vite.config.ts`. Update that file:\n\n   ```ts\n   ...\n   plugins: [react()],\n   server: {\n    allowedHosts: ['your-fly-app-name.fly.dev', 'localhost', '127.0.0.1'],\n   },\n   ...\n   ```\n\n   Then `fly deploy` to redeploy it.\n\n## Self-hosting Convex on Fly.io\n\nIf you want to self-host the Convex backend on Fly.io, you can follow these steps:\n\n1. Deploy a self-hosted Convex instance:\n\n   ```sh\n   cd fly/backend\n   fly launch\n   # Say `y` to copy the configuration, and `n` to tweak the settings.\n   ```\n\n   Note the Fly URL of the deployed backend. Like\n   `https://ai-town-convex-backend-1234567890.fly.dev`.\n\n   Note: you can't scale the backends to more than 1 machine. It is stateful.\n\n2. Set the environment variables `CONVEX_CLOUD_ORIGIN` and `CONVEX_SITE_ORIGIN` for your backend.\n\n   These environment variables are used by the backend so it knows where it is hosted. Inside your\n   Convex backend functions, you can access the backend's URL with `process.env.CONVEX_CLOUD_URL`\n   for the Convex client API and `process.env.CONVEX_SITE_URL` for the HTTP API.\n\n   ```sh\n   fly secrets set CONVEX_CLOUD_ORIGIN=\"<fly-backend-url>\" CONVEX_SITE_ORIGIN=\"<fly-backend-url>/http\"\n   ```\n\n   Now your backend knows its base URL so it can generate URLs that point back to itself. This is\n   especially useful for libraries registering webhooks and\n   [Convex Auth](https://labs.convex.dev/auth) for generating auth callbacks.\n\n3. Generate an admin key.\n\n   ```sh\n   fly ssh console --command \"./generate_admin_key.sh\"\n   ```\n\n   Unless you edited the app's `fly.toml`, the name is `ai-town-convex-backend`. If you specified a\n   different name, replace `ai-town-convex-backend` with it.\n\n   This admin key will be used to authorize the CLI and access the dashboard.\n\n4. In the root directory of the AI Town repository (`cd ../..`), create a `.env.local` file with the\n   following variables:\n\n   ```sh\n   CONVEX_SELF_HOSTED_URL=\"<fly-backend-url>\"\n   CONVEX_SELF_HOSTED_ADMIN_KEY=\"<your-admin-key>\"\n   ```\n\n5. Deploy your Convex functions to the backend using the `convex` CLI from the project root.\n\n   To deploy the AI Town functions to the backend and start the game engine:\n\n   ```sh\n   npx convex dev --run init --once\n   ```\n\n   To continuously deploy code for development:\n\n   ```sh\n   npx convex dev\n   ```\n\n6. Deploy the frontend app to Fly.io from the root directory. See [above](#setup) for details.\n\n   ```sh\n   fly launch -e VITE_CONVEX_URL=<fly-backend-url>\n   ```\n\n7. (Optional) Deploy the Convex dashboard to monitor the self-hosted Convex backend.\n\n   You can either run the dashboard locally and talk to the Fly.io backend, or you can also host the\n   backend on Fly.io. In either case, log in with the admin key you generated earlier.\n\n   **Running the dashboard locally:**\n\n   ```sh\n   docker run -e 'NEXT_PUBLIC_DEPLOYMENT_URL=<fly-backend-url>' -p '6791:6791' 'ghcr.io/get-convex/convex-dashboard:latest'\n   ```\n\n   You should now see the dashboard running at `http://localhost:6791`.\n\n   **Hosting the dashboard on Fly.io:**\n\n   ```sh\n   cd fly/dashboard\n   fly launch -e NEXT_PUBLIC_DEPLOYMENT_URL=<fly-backend-url>\n   fly scale count 1 # You probably don't need more than 1 machine for the dashboard\n   ```\n\n   You should now see the dashboard running at the URL provided by `fly launch`.\n"
  },
  {
    "path": "fly/backend/fly.toml",
    "content": "# fly.toml app configuration file generated for convex-backend on 2025-02-12T15:17:28-08:00\n#\n# See https://fly.io/docs/reference/configuration/ for information about how to use this file.\n#\n\napp = 'convex-backend'\nprimary_region = 'iad'\n\n[build]\nimage = 'ghcr.io/get-convex/convex-backend:4499dd4fd7f2148687a7774599c613d052950f46'\n\n[env]\nTMPDIR = '/convex/data/tmp'\n\n[[mounts]]\nsource = 'convex_data'\ndestination = '/convex/data'\n\n[http_service]\ninternal_port = 3210\nforce_https = true\nauto_stop_machines = 'stop'\nauto_start_machines = true\nmin_machines_running = 1\nprocesses = ['app']\n\n[[http_service.checks]]\ninterval = '5s'\ntimeout = '30s'\ngrace_period = '5s'\nmethod = 'GET'\npath = '/version'\nprotocol = 'http'\n\n[[vm]]\nmemory = '1gb'\ncpu_kind = 'shared'\ncpus = 1\n"
  },
  {
    "path": "fly/dashboard/fly.toml",
    "content": "# fly.toml app configuration file generated for convex-dashboard on 2025-02-12T15:24:01-08:00\n#\n# See https://fly.io/docs/reference/configuration/ for information about how to use this file.\n#\n\napp = 'convex-dashboard'\nprimary_region = 'iad'\n\n[build]\nimage = 'ghcr.io/get-convex/convex-dashboard:4499dd4fd7f2148687a7774599c613d052950f46'\n\n[[services]]\nprotocol = 'tcp'\ninternal_port = 6791\n\n[[services.ports]]\nport = 80\nhandlers = ['http']\nforce_https = true\n\n[[services.ports]]\nport = 443\nhandlers = ['tls', 'http']\n\n[[vm]]\nmemory = '1gb'\ncpu_kind = 'shared'\ncpus = 1\n"
  },
  {
    "path": "index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/x-icon\" sizes=\"any\" href=\"/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>AI Town</title>\n    <meta\n      name=\"description\"\n      content=\"A virtual town where AI characters live, chat and socialize\"\n    />\n    <script defer data-domain=\"convex.dev\" src=\"https://plausible.io/js/script.js\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "jest.config.ts",
    "content": "import type { JestConfigWithTsJest } from 'ts-jest';\n\nconst jestConfig: JestConfigWithTsJest = {\n  preset: 'ts-jest/presets/default-esm',\n};\nexport default jestConfig;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"ai-town\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"npm-run-all --parallel dev:backend dev:frontend\",\n    \"build\": \"tsc && vite build\",\n    \"lint\": \"eslint .\",\n    \"predev\": \"convex dev --run init --until-success\",\n    \"dev:backend\": \"convex dev --tail-logs\",\n    \"dev:frontend\": \"vite\",\n    \"dashboard\": \"convex dashboard\",\n    \"test\": \"NODE_OPTIONS=--experimental-vm-modules jest --verbose\",\n    \"level-editor\": \"vite src/editor/\",\n    \"le\": \"vite src/editor/\"\n  },\n  \"dependencies\": {\n    \"@clerk/clerk-react\": \"^4.27.0\",\n    \"@pixi/react\": \"^7.1.0\",\n    \"@pixi/sound\": \"^5.2.0\",\n    \"@tailwindcss/forms\": \"^0.5.3\",\n    \"@types/node\": \"20.2.5\",\n    \"@types/react\": \"18.2.8\",\n    \"@types/react-dom\": \"18.2.4\",\n    \"clsx\": \"^2.0.0\",\n    \"convex\": \"^1.19.2\",\n    \"dotenv\": \"^16.1.4\",\n    \"eslint\": \"8.42.0\",\n    \"hnswlib-node\": \"^1.4.2\",\n    \"pixi-viewport\": \"^5.0.1\",\n    \"pixi.js\": \"^7.2.4\",\n    \"react\": \"18.2.0\",\n    \"react-dom\": \"18.2.0\",\n    \"react-github-btn\": \"^1.4.0\",\n    \"react-modal\": \"^3.16.1\",\n    \"react-toastify\": \"^9.1.3\",\n    \"replicate\": \"^0.18.0\",\n    \"typescript\": \"5.1.3\",\n    \"update-browserslist-db\": \"^1.1.2\",\n    \"uplot\": \"^1.6.25\",\n    \"usehooks-ts\": \"^2.9.1\"\n  },\n  \"devDependencies\": {\n    \"@flydotio/dockerfile\": \"^0.2.14\",\n    \"@types/css-font-loading-module\": \"^0.0.8\",\n    \"@types/jest\": \"^29.5.4\",\n    \"@types/react-modal\": \"^3.16.0\",\n    \"@types/ws\": \"^8.5.5\",\n    \"@typescript-eslint/eslint-plugin\": \"^6.4.1\",\n    \"@typescript-eslint/parser\": \"^6.4.1\",\n    \"@vitejs/plugin-react\": \"^4.0.4\",\n    \"autoprefixer\": \"^10.4.15\",\n    \"concurrently\": \"^8.2.0\",\n    \"jest\": \"^29.7.0\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"postcss\": \"^8.4.28\",\n    \"prettier\": \"^3.4.2\",\n    \"tailwindcss\": \"^3.3.3\",\n    \"ts-jest\": \"^29.1.1\",\n    \"ts-node\": \"^10.9.1\",\n    \"vite\": \"^4.4.9\"\n  },\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "export default {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n};\n"
  },
  {
    "path": "public/assets/tilemap.json",
    "content": "{\n  \"compressionlevel\": -1,\n  \"height\": 40,\n  \"infinite\": false,\n  \"layers\": [\n    {\n      \"data\": [\n        8201, 8202, 8203, 8204, 8202, 8203, 8204, 8202, 8203, 8204, 8205, 8202, 8203, 8204, 8205,\n        6366, 6267, 6268, 6369, 6369, 6269, 6268, 6368, 6369, 6268, 6268, 6368, 6269, 6369, 6268,\n        6269, 6268, 6268, 6268, 6269, 6269, 6268, 6368, 6369, 6268, 8301, 8302, 8303, 8304, 8302,\n        8303, 8303, 8302, 8303, 8304, 8305, 8302, 8303, 8304, 8305, 6366, 6367, 6269, 6369, 6268,\n        6269, 6268, 6369, 6268, 6368, 6369, 6268, 6269, 6369, 6368, 6269, 6269, 6269, 6269, 6269,\n        6269, 6268, 6269, 6368, 6368, 8401, 8402, 8403, 8404, 8402, 8403, 8403, 8402, 8403, 8404,\n        8405, 8402, 8403, 8404, 8405, 6366, 6367, 6368, 6369, 6268, 6369, 6368, 6369, 6268, 6369,\n        6369, 6268, 6369, 6269, 6369, 6368, 6268, 6369, 6368, 6269, 6269, 6368, 6368, 6268, 6368,\n        8501, 8502, 8503, 8504, 8502, 8503, 8503, 8502, 8503, 8504, 8505, 8502, 8503, 8504, 8505,\n        6266, 6267, 6368, 6268, 6268, 6269, 6269, 6369, 6269, 6369, 6269, 6268, 6269, 6368, 6369,\n        6269, 6368, 6369, 6368, 6368, 6269, 6269, 6268, 6369, 6368, 8601, 8602, 8603, 8602, 8602,\n        8603, 8603, 8602, 8603, 8604, 8605, 8602, 8603, 8604, 8605, 6366, 6367, 6269, 6269, 6369,\n        6369, 6368, 6369, 6369, 6369, 6269, 6268, 6369, 6269, 6268, 6368, 6268, 6368, 6269, 6369,\n        6369, 6368, 6368, 6369, 6369, 8701, 8702, 8703, 8704, 8702, 8703, 8703, 8702, 8703, 8704,\n        8705, 8702, 8703, 8704, 8705, 6366, 6367, 6269, 6369, 6269, 6368, 6369, 6368, 6269, 6369,\n        6368, 6368, 6368, 6368, 6269, 6268, 6368, 6368, 6369, 6369, 6268, 6368, 6368, 6268, 6368,\n        8801, 8802, 8803, 8804, 8802, 8803, 8803, 8802, 8803, 8804, 8805, 8802, 8803, 8804, 8805,\n        6266, 6367, 6368, 6369, 6269, 6269, 6269, 6268, 6268, 6269, 6369, 6269, 6368, 6268, 6369,\n        6369, 6369, 6368, 6369, 6368, 6368, 6368, 6369, 6269, 6269, 8901, 8902, 8903, 8904, 8902,\n        8903, 8903, 8902, 8903, 8904, 8905, 8902, 8903, 8904, 8905, 6366, 6367, 6368, 6368, 6269,\n        6369, 6269, 6369, 6369, 6368, 6269, 6268, 6368, 6268, 6269, 6268, 6268, 6369, 6369, 6368,\n        6269, 6268, 6369, 6368, 0, 9001, 9002, 9003, 9004, 9002, 9003, 9003, 9002, 9003, 9004, 9005,\n        9002, 9003, 9004, 9005, 6366, 6367, 6368, 6269, 6269, 6368, 6368, 6368, 6368, 6368, 6269,\n        6269, 6368, 6268, 6369, 6369, 6268, 6369, 6269, 6368, 6369, 6269, 6369, 6368, 0, 202, 203,\n        204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 6466, 6467, 6469, 6468,\n        6469, 6468, 6469, 6469, 6469, 6469, 6469, 6469, 6468, 6469, 6468, 6468, 6469, 6469, 6468,\n        6469, 6468, 6469, 6468, 6468, 6468, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304,\n        305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302,\n        303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204,\n        205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202,\n        203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204,\n        205\n      ],\n      \"height\": 40,\n      \"id\": 1,\n      \"name\": \"terrain\",\n      \"opacity\": 1,\n      \"type\": \"tilelayer\",\n      \"visible\": true,\n      \"width\": 40,\n      \"x\": 0,\n      \"y\": 0\n    },\n    {\n      \"data\": [\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      ],\n      \"height\": 40,\n      \"id\": 2,\n      \"name\": \"bridge\",\n      \"opacity\": 1,\n      \"type\": \"tilelayer\",\n      \"visible\": true,\n      \"width\": 40,\n      \"x\": 0,\n      \"y\": 0\n    },\n    {\n      \"data\": [\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4903, 4904, 4905, 4906, 4907, 4908, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        5003, 5004, 5005, 5006, 5007, 5008, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        4909, 4910, 4911, 4912, 4913, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5103, 5104, 5105, 5106, 5107,\n        5108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5009, 5010, 5011, 5012, 5013, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5203, 5204, 5205, 5206, 5207, 5208, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 5109, 5110, 5111, 5112, 5113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        5303, 5304, 5305, 5306, 5307, 5308, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        5209, 5210, 5211, 5212, 5213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5403, 5404, 5405, 5406, 5407,\n        2147488562, 2147488561, 2147488560, 2147488559, 2147488558, 2147488557, 0, 2936, 2937, 3138,\n        3139, 3138, 3139, 3138, 3139, 3138, 3139, 2940, 2941, 5309, 5310, 5311, 5312, 5313, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 5503, 5504, 5505, 5506, 5507, 2147488662, 2147488661, 2147488660,\n        2147488659, 2147488658, 2147488657, 0, 3036, 3037, 3238, 3239, 3238, 3239, 3238, 3239, 3238,\n        3239, 3040, 3041, 5409, 5410, 5411, 5412, 5413, 0, 0, 0, 5423, 5424, 0, 0, 5425, 5426, 0, 0,\n        0, 0, 0, 0, 0, 2147488762, 2147488761, 2147488760, 2147488759, 2147488758, 2147488757, 0,\n        2740, 2741, 0, 0, 0, 0, 0, 0, 0, 0, 2740, 2741, 5509, 5510, 5511, 5512, 5513, 0, 0, 0, 5523,\n        5524, 0, 0, 5525, 5526, 0, 0, 0, 0, 0, 0, 0, 2147488862, 2147488861, 2147488860, 2147488859,\n        2147488858, 2147488857, 0, 2840, 2841, 0, 0, 0, 0, 0, 0, 0, 0, 2840, 2841, 5019, 5020, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2147488962, 2147488961, 2147488960,\n        2147488959, 2147488958, 2147488957, 0, 2740, 2741, 0, 0, 0, 0, 0, 0, 0, 0, 2740, 2741, 5119,\n        5120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5403, 2147489062, 2147489061,\n        2147489060, 2147489059, 2147489058, 5025, 5026, 2840, 2841, 0, 0, 0, 0, 0, 0, 0, 0, 2840,\n        2841, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5425, 5426, 0, 0, 0, 5503, 2147489162,\n        2147489161, 2147489160, 2147489159, 2147489158, 5125, 5126, 3136, 3137, 3138, 3139, 3138,\n        3139, 3138, 3139, 3138, 3139, 3140, 3141, 4825, 4826, 0, 0, 0, 1813, 1814, 1813, 1814, 1813,\n        1814, 1813, 1814, 1813, 1814, 1813, 1814, 1813, 1814, 1813, 1813, 1813, 1814, 1813, 1813,\n        1813, 1814, 1813, 3236, 3237, 3238, 3239, 3238, 3239, 3238, 3239, 3238, 3239, 3240, 3241,\n        4925, 4926, 0, 0, 0, 1913, 1914, 1913, 1914, 1913, 1914, 1913, 1914, 1913, 1914, 1913, 1914,\n        1913, 1914, 2013, 2013, 2013, 2114, 2013, 2013, 2013, 2113, 1913, 1914, 1914, 1913, 1914,\n        1913, 1914, 1913, 1914, 1913, 1914, 1914, 1914, 1915, 2116, 4822, 4823, 4824, 2013, 2014,\n        2013, 2014, 2013, 2014, 2013, 2014, 2013, 2113, 2114, 2113, 2114, 2114, 2013, 2013, 2114,\n        2114, 2114, 2013, 2013, 2013, 2114, 2113, 2114, 2014, 2114, 2014, 2014, 2013, 2113, 2114,\n        2113, 2013, 2014, 2015, 4921, 4922, 4923, 4924, 2113, 2114, 2113, 2114, 2113, 2114, 2113,\n        2114, 2113, 2114, 2113, 2114, 2113, 2114, 2113, 2113, 2113, 2114, 2113, 2113, 2113, 2114,\n        2113, 2114, 2114, 2113, 2114, 2113, 2114, 2113, 2114, 2113, 2114, 2113, 2114, 2115, 5021,\n        5022, 5023, 5024, 2213, 2214, 2213, 2214, 2213, 2214, 2214, 2213, 2213, 2214, 2214, 2214,\n        2213, 2213, 2213, 2214, 2213, 2214, 2214, 2214, 2214, 2214, 2214, 2213, 2213, 2214, 2213,\n        2213, 2214, 2213, 2214, 1819, 2113, 2014, 2114, 2015, 5121, 5122, 5123, 5124, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1919, 1920,\n        2014, 2014, 2015, 0, 5423, 5424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2011, 2112, 2013, 2113, 2115, 2116, 5523, 5524, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2011,\n        2012, 2013, 2013, 2115, 2016, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2012, 2013, 2014, 2015, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2011, 2012,\n        2013, 2113, 2015, 2016, 5425, 5426, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2111, 2112, 2014, 2114, 2115, 2116, 5525, 5526, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        2011, 2012, 2013, 2013, 2015, 2016, 0, 0, 0, 0, 0, 2147488556, 2147488555, 2147488554,\n        2147488553, 2147488552, 2147488551, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 2011, 2112, 2114, 2114, 2015, 2016, 0, 0, 0, 0, 0, 2147488656, 2147488655,\n        2147488654, 2147488653, 2147488652, 2147488651, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 2111, 2112, 2114, 2113, 2015, 2016, 0, 0, 0, 0, 0, 2147488756,\n        2147488755, 2147488754, 2147488753, 2147488752, 2147488751, 0, 0, 0, 0, 0, 5425, 5426, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2011, 2012, 2114, 2014, 2115, 2016, 5017, 5018, 0,\n        0, 0, 2147488856, 2147488855, 2147488854, 2147488853, 2147488852, 2147488851, 0, 0, 0, 0, 0,\n        5525, 5526, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2111, 2112, 2014, 2013, 2115,\n        2116, 5117, 5118, 0, 0, 0, 2147488956, 2147488955, 2147488954, 2147488953, 2147488952,\n        2147488951, 0, 0, 0, 5225, 5226, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5225, 5226, 0, 0, 0, 0,\n        2111, 2112, 2013, 2013, 2115, 2016, 0, 0, 0, 0, 0, 2147489056, 2147489055, 2147489054,\n        2147489053, 2147489052, 2147489051, 0, 0, 0, 5325, 5326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        5325, 5326, 0, 0, 0, 0, 2111, 2112, 2114, 2014, 2115, 2116, 0, 0, 0, 0, 0, 2147489156,\n        2147489155, 2147489154, 2147489153, 2147489152, 2147489151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 5425, 5426, 0, 0, 0, 0, 2111, 2112, 2113, 2013, 2015, 2016, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 5217, 5218, 5219, 5220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5525, 5526,\n        0, 0, 0, 0, 2011, 2012, 2113, 2113, 2015, 2016, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5317, 5318,\n        5319, 5320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2111, 2112, 2013, 2013,\n        2115, 2116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5417, 5418, 5419, 5420, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 4825, 4826, 0, 0, 0, 0, 0, 2011, 2012, 2013, 2114, 2115, 2116, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 5517, 5518, 5519, 5520, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4925, 4926, 0, 0,\n        0, 0, 0, 2011, 2012, 2013, 2114, 2015, 2016, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2111, 2112, 2113, 2114, 2015, 0, 0, 0,\n        0\n      ],\n      \"height\": 40,\n      \"id\": 3,\n      \"name\": \"deco\",\n      \"opacity\": 1,\n      \"type\": \"tilelayer\",\n      \"visible\": true,\n      \"width\": 40,\n      \"x\": 0,\n      \"y\": 0\n    },\n    {\n      \"draworder\": \"topdown\",\n      \"id\": 4,\n      \"name\": \"monsters\",\n      \"objects\": [\n        {\n          \"height\": 0,\n          \"id\": \"Alex\",\n          \"name\": \"treant\",\n          \"point\": true,\n          \"rotation\": 0,\n          \"type\": \"\",\n          \"visible\": true,\n          \"width\": 0,\n          \"x\": 500.811,\n          \"y\": 224.316\n        },\n        {\n          \"height\": 0,\n          \"id\": \"Sebastian\",\n          \"name\": \"treant\",\n          \"point\": true,\n          \"rotation\": 0,\n          \"type\": \"\",\n          \"visible\": true,\n          \"width\": 0,\n          \"x\": 423.035,\n          \"y\": 216.136\n        },\n        {\n          \"height\": 0,\n          \"id\": 15,\n          \"name\": \"mole\",\n          \"point\": true,\n          \"rotation\": 0,\n          \"type\": \"\",\n          \"visible\": true,\n          \"width\": 0,\n          \"x\": 532.347,\n          \"y\": 369.656\n        }\n      ],\n      \"opacity\": 1,\n      \"type\": \"objectgroup\",\n      \"visible\": true,\n      \"x\": 0,\n      \"y\": 0\n    },\n    {\n      \"draworder\": \"topdown\",\n      \"id\": 5,\n      \"name\": \"npcs\",\n      \"objects\": [\n        {\n          \"height\": 0,\n          \"id\": 6,\n          \"name\": \"npc\",\n          \"point\": true,\n          \"properties\": [\n            {\n              \"name\": \"message\",\n              \"type\": \"string\",\n              \"value\": \"Hello! Watch out for the dangerous treants!\"\n            }\n          ],\n          \"rotation\": 0,\n          \"type\": \"\",\n          \"visible\": true,\n          \"width\": 0,\n          \"x\": 58.2323,\n          \"y\": 146.777\n        },\n        {\n          \"height\": 0,\n          \"id\": 7,\n          \"name\": \"npc\",\n          \"point\": true,\n          \"properties\": [\n            {\n              \"name\": \"message\",\n              \"type\": \"string\",\n              \"value\": \"Hello! You are doing fine :)\"\n            }\n          ],\n          \"rotation\": 0,\n          \"type\": \"\",\n          \"visible\": true,\n          \"width\": 0,\n          \"x\": 494.34,\n          \"y\": 459.29\n        }\n      ],\n      \"opacity\": 1,\n      \"type\": \"objectgroup\",\n      \"visible\": true,\n      \"x\": 0,\n      \"y\": 0\n    },\n    {\n      \"draworder\": \"topdown\",\n      \"id\": 6,\n      \"name\": \"zones\",\n      \"objects\": [\n        {\n          \"height\": 44.6713,\n          \"id\": 11,\n          \"name\": \"\",\n          \"properties\": [\n            {\n              \"name\": \"comesBackFrom\",\n              \"type\": \"string\",\n              \"value\": \"right\"\n            },\n            {\n              \"name\": \"scene\",\n              \"type\": \"string\",\n              \"value\": \"SecondLevel\"\n            },\n            {\n              \"name\": \"type\",\n              \"type\": \"string\",\n              \"value\": \"CHANGE_SCENE\"\n            }\n          ],\n          \"rotation\": 0,\n          \"type\": \"\",\n          \"visible\": true,\n          \"width\": 13.5609,\n          \"x\": 355.775,\n          \"y\": 408.424\n        }\n      ],\n      \"opacity\": 1,\n      \"type\": \"objectgroup\",\n      \"visible\": true,\n      \"x\": 0,\n      \"y\": 0\n    }\n  ],\n  \"nextlayerid\": 9,\n  \"nextobjectid\": 16,\n  \"orientation\": \"orthogonal\",\n  \"renderorder\": \"right-down\",\n  \"tiledversion\": \"1.10.1\",\n  \"tileheight\": 16,\n  \"tilesets\": [\n    {\n      \"columns\": 100,\n      \"firstgid\": 1,\n      \"image\": \"environment/rpg-tileset.png\",\n      \"imageheight\": 1600,\n      \"imagewidth\": 1600,\n      \"margin\": 0,\n      \"name\": \"rpg-tileset\",\n      \"spacing\": 0,\n      \"tilecount\": 10000,\n      \"tileheight\": 16,\n      \"tiles\": [\n        {\n          \"id\": 0,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 1,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 3,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 4,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 5,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 51,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 52,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 53,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 54,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 55,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 56,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 57,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 58,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 100,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 101,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 102,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 103,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 104,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 105,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 151,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 152,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 153,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 154,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 155,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 156,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 157,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 158,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 200,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 201,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 202,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 203,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 204,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 205,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 251,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 252,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 253,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 254,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 255,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 256,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n\n        {\n          \"id\": 257,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 258,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 300,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 301,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 302,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 303,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 304,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 305,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 351,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 352,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 353,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 354,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 355,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 356,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 357,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 358,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 400,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 401,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 402,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 403,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 404,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 405,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 451,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 452,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 453,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 454,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 455,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 456,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 457,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 458,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 500,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 501,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 502,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 503,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 504,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 505,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 551,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 552,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 553,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 554,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n\n        {\n          \"id\": 555,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 556,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 557,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 558,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 601,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 602,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 603,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 604,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 607,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 608,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 651,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 652,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 653,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 654,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 655,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 656,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 657,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 658,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 701,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 702,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 703,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 704,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 706,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 707,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 708,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 709,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 711,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 712,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 713,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 714,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 801,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 802,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 803,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 804,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 806,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 807,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 808,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 809,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 811,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 812,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n\n        {\n          \"id\": 813,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 814,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 901,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 902,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 903,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 904,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 907,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 908,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 911,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 912,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 913,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 914,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 1001,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 1002,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 1003,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 1004,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 1011,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 1012,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 1013,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 1014,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 1811,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 1812,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 1813,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 1814,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 1911,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 1912,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 1913,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 1914,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2011,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2012,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2013,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2014,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2111,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2112,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2113,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2114,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2211,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2212,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2213,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 2214,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n\n        {\n          \"id\": 2737,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2738,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2739,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2740,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2837,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2838,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2839,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2840,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2935,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2936,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2937,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2938,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2939,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 2940,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3035,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3036,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3037,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3038,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3039,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3040,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3135,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3136,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3137,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3138,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3139,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3140,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3235,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3236,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3237,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3238,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3239,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 3240,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4769,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4770,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4771,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4772,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4773,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4774,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4816,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4817,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n\n        {\n          \"id\": 4818,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4819,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4821,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4822,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4869,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4870,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4871,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4872,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4873,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4874,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4902,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4903,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4904,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4905,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4906,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4909,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4910,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4911,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4912,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4916,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4917,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4918,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4919,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4920,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4921,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4922,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4923,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4969,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4970,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4971,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4972,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4973,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4974,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4975,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 4976,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5002,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5003,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5004,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5005,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5006,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n\n        {\n          \"id\": 5007,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5008,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5009,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5010,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5011,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5012,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5013,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5016,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5017,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5018,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5019,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5020,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5021,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5022,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5023,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5024,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5025,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5069,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5070,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5071,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5072,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5073,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5074,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5075,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5076,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5102,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5103,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5104,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5105,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5106,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5107,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5108,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5109,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5110,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5111,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5112,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5113,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5116,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5117,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5118,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n\n        {\n          \"id\": 5119,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5120,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5121,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5122,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5123,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5124,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5125,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5169,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5170,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5171,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5172,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5173,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5174,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5175,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5176,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5202,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5203,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5204,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5205,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5206,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5207,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5208,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5209,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5210,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5211,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5212,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5213,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5216,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5217,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5218,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5219,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5220,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5222,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5269,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5270,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5271,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5272,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5273,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5274,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5275,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n\n        {\n          \"id\": 5276,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5302,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5303,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5304,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5305,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5306,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5307,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5308,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5309,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5310,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5311,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5312,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5313,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5316,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5317,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5318,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5319,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5321,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5323,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5403,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5404,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5405,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5406,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5409,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5410,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5411,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5412,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5416,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5417,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5418,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5419,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5420,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5421,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5503,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5504,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5505,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5506,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5510,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5511,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5516,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n\n        {\n          \"id\": 5517,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5518,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5519,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5520,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 5521,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6065,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6070,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6075,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6076,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6077,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6078,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6079,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6080,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6165,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6166,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6167,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6168,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6169,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6170,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6171,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6172,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6173,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6174,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6175,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6176,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6177,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6178,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6179,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6180,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6265,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6266,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6267,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6268,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6269,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6270,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6271,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6272,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6273,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6274,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6275,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n\n        {\n          \"id\": 6276,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6277,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6278,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6279,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6280,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6365,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6366,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6367,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6368,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6369,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6370,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6371,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6372,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6373,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6374,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6375,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6376,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6377,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6378,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6379,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6380,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6465,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6466,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6467,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6468,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6469,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6470,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6471,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6472,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6473,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 6474,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6475,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6476,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6477,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6478,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6479,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 6480,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": false\n            }\n          ]\n        },\n        {\n          \"id\": 8600,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8601,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8602,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n\n        {\n          \"id\": 8603,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8604,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8605,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8700,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8701,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8702,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8703,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8704,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8705,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8800,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8801,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8802,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8803,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8804,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8805,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8900,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8901,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8902,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8903,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8904,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 8905,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 9000,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 9001,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 9002,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 9003,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 9004,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        },\n        {\n          \"id\": 9005,\n          \"properties\": [\n            {\n              \"name\": \"collides\",\n              \"type\": \"bool\",\n              \"value\": true\n            }\n          ]\n        }\n      ],\n      \"tilewidth\": 16\n    }\n  ],\n  \"tilewidth\": 16,\n  \"type\": \"map\",\n  \"version\": \"1.10\",\n  \"width\": 40\n}\n"
  },
  {
    "path": "src/App.tsx",
    "content": "import Game from './components/Game.tsx';\n\nimport { ToastContainer } from 'react-toastify';\nimport a16zImg from '../assets/a16z.png';\nimport convexImg from '../assets/convex.svg';\nimport starImg from '../assets/star.svg';\nimport helpImg from '../assets/help.svg';\n// import { UserButton } from '@clerk/clerk-react';\n// import { Authenticated, Unauthenticated } from 'convex/react';\n// import LoginButton from './components/buttons/LoginButton.tsx';\nimport { useState } from 'react';\nimport ReactModal from 'react-modal';\nimport MusicButton from './components/buttons/MusicButton.tsx';\nimport Button from './components/buttons/Button.tsx';\nimport InteractButton from './components/buttons/InteractButton.tsx';\nimport FreezeButton from './components/FreezeButton.tsx';\nimport { MAX_HUMAN_PLAYERS } from '../convex/constants.ts';\nimport PoweredByConvex from './components/PoweredByConvex.tsx';\n\nexport default function Home() {\n  const [helpModalOpen, setHelpModalOpen] = useState(false);\n  return (\n    <main className=\"relative flex min-h-screen flex-col items-center justify-between font-body game-background\">\n      <PoweredByConvex />\n\n      <ReactModal\n        isOpen={helpModalOpen}\n        onRequestClose={() => setHelpModalOpen(false)}\n        style={modalStyles}\n        contentLabel=\"Help modal\"\n        ariaHideApp={false}\n      >\n        <div className=\"font-body\">\n          <h1 className=\"text-center text-6xl font-bold font-display game-title\">Help</h1>\n          <p>\n            Welcome to AI town. AI town supports both anonymous <i>spectators</i> and logged in{' '}\n            <i>interactivity</i>.\n          </p>\n          <h2 className=\"text-4xl mt-4\">Spectating</h2>\n          <p>\n            Click and drag to move around the town, and scroll in and out to zoom. You can click on\n            an individual character to view its chat history.\n          </p>\n          <h2 className=\"text-4xl mt-4\">Interactivity</h2>\n          <p>\n            If you log in, you can join the simulation and directly talk to different agents! After\n            logging in, click the \"Interact\" button, and your character will appear somewhere on the\n            map with a highlighted circle underneath you.\n          </p>\n          <p className=\"text-2xl mt-2\">Controls:</p>\n          <p className=\"mt-4\">Click to navigate around.</p>\n          <p className=\"mt-4\">\n            To talk to an agent, click on them and then click \"Start conversation,\" which will ask\n            them to start walking towards you. Once they're nearby, the conversation will start, and\n            you can speak to each other. You can leave at any time by closing the conversation pane\n            or moving away. They may propose a conversation to you - you'll see a button to accept\n            in the messages panel.\n          </p>\n          <p className=\"mt-4\">\n            AI town only supports {MAX_HUMAN_PLAYERS} humans at a time. If you're idle for five\n            minutes, you'll be automatically removed from the simulation.\n          </p>\n        </div>\n      </ReactModal>\n      {/*<div className=\"p-3 absolute top-0 right-0 z-10 text-2xl\">\n        <Authenticated>\n          <UserButton afterSignOutUrl=\"/ai-town\" />\n        </Authenticated>\n\n        <Unauthenticated>\n          <LoginButton />\n        </Unauthenticated>\n      </div> */}\n\n      <div className=\"w-full lg:h-screen min-h-screen relative isolate overflow-hidden lg:p-8 shadow-2xl flex flex-col justify-start\">\n        <h1 className=\"mx-auto text-4xl p-3 sm:text-8xl lg:text-9xl font-bold font-display leading-none tracking-wide game-title w-full text-left sm:text-center sm:w-auto\">\n          AI Town\n        </h1>\n\n        <div className=\"max-w-xs md:max-w-xl lg:max-w-none mx-auto my-4 text-center text-base sm:text-xl md:text-2xl text-white leading-tight shadow-solid\">\n          A virtual town where AI characters live, chat and socialize.\n          {/* <Unauthenticated>\n            <div className=\"my-1.5 sm:my-0\" />\n            Log in to join the town\n            <br className=\"block sm:hidden\" /> and the conversation!\n          </Unauthenticated> */}\n        </div>\n\n        <Game />\n\n        <footer className=\"justify-end bottom-0 left-0 w-full flex items-center mt-4 gap-3 p-6 flex-wrap pointer-events-none\">\n          <div className=\"flex gap-4 flex-grow pointer-events-none\">\n            <FreezeButton />\n            <MusicButton />\n            <Button href=\"https://github.com/a16z-infra/ai-town\" imgUrl={starImg}>\n              Star\n            </Button>\n            <InteractButton />\n            <Button imgUrl={helpImg} onClick={() => setHelpModalOpen(true)}>\n              Help\n            </Button>\n          </div>\n          <a href=\"https://a16z.com\">\n            <img className=\"w-8 h-8 pointer-events-auto\" src={a16zImg} alt=\"a16z\" />\n          </a>\n          <a href=\"https://convex.dev/c/ai-town\">\n            <img className=\"w-20 h-8 pointer-events-auto\" src={convexImg} alt=\"Convex\" />\n          </a>\n        </footer>\n        <ToastContainer position=\"bottom-right\" autoClose={2000} closeOnClick theme=\"dark\" />\n      </div>\n    </main>\n  );\n}\n\nconst modalStyles = {\n  overlay: {\n    backgroundColor: 'rgb(0, 0, 0, 75%)',\n    zIndex: 12,\n  },\n  content: {\n    top: '50%',\n    left: '50%',\n    right: 'auto',\n    bottom: 'auto',\n    marginRight: '-50%',\n    transform: 'translate(-50%, -50%)',\n    maxWidth: '50%',\n\n    border: '10px solid rgb(23, 20, 33)',\n    borderRadius: '0',\n    background: 'rgb(35, 38, 58)',\n    color: 'white',\n    fontFamily: '\"Upheaval Pro\", \"sans-serif\"',\n  },\n};\n"
  },
  {
    "path": "src/components/Character.tsx",
    "content": "import { BaseTexture, ISpritesheetData, Spritesheet } from 'pixi.js';\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport { AnimatedSprite, Container, Graphics, Text } from '@pixi/react';\nimport * as PIXI from 'pixi.js';\n\nexport const Character = ({\n  textureUrl,\n  spritesheetData,\n  x,\n  y,\n  orientation,\n  isMoving = false,\n  isThinking = false,\n  isSpeaking = false,\n  emoji = '',\n  isViewer = false,\n  speed = 0.1,\n  onClick,\n}: {\n  // Path to the texture packed image.\n  textureUrl: string;\n  // The data for the spritesheet.\n  spritesheetData: ISpritesheetData;\n  // The pose of the NPC.\n  x: number;\n  y: number;\n  orientation: number;\n  isMoving?: boolean;\n  // Shows a thought bubble if true.\n  isThinking?: boolean;\n  // Shows a speech bubble if true.\n  isSpeaking?: boolean;\n  emoji?: string;\n  // Highlights the player.\n  isViewer?: boolean;\n  // The speed of the animation. Can be tuned depending on the side and speed of the NPC.\n  speed?: number;\n  onClick: () => void;\n}) => {\n  const [spriteSheet, setSpriteSheet] = useState<Spritesheet>();\n  useEffect(() => {\n    const parseSheet = async () => {\n      const sheet = new Spritesheet(\n        BaseTexture.from(textureUrl, {\n          scaleMode: PIXI.SCALE_MODES.NEAREST,\n        }),\n        spritesheetData,\n      );\n      await sheet.parse();\n      setSpriteSheet(sheet);\n    };\n    void parseSheet();\n  }, []);\n\n  // The first \"left\" is \"right\" but reflected.\n  const roundedOrientation = Math.floor(orientation / 90);\n  const direction = ['right', 'down', 'left', 'up'][roundedOrientation];\n\n  // Prevents the animation from stopping when the texture changes\n  // (see https://github.com/pixijs/pixi-react/issues/359)\n  const ref = useRef<PIXI.AnimatedSprite | null>(null);\n  useEffect(() => {\n    if (isMoving) {\n      ref.current?.play();\n    }\n  }, [direction, isMoving]);\n\n  if (!spriteSheet) return null;\n\n  let blockOffset = { x: 0, y: 0 };\n  switch (roundedOrientation) {\n    case 2:\n      blockOffset = { x: -20, y: 0 };\n      break;\n    case 0:\n      blockOffset = { x: 20, y: 0 };\n      break;\n    case 3:\n      blockOffset = { x: 0, y: -20 };\n      break;\n    case 1:\n      blockOffset = { x: 0, y: 20 };\n      break;\n  }\n\n  return (\n    <Container x={x} y={y} interactive={true} pointerdown={onClick} cursor=\"pointer\">\n      {isThinking && (\n        // TODO: We'll eventually have separate assets for thinking and speech animations.\n        <Text x={-20} y={-10} scale={{ x: -0.8, y: 0.8 }} text={'💭'} anchor={{ x: 0.5, y: 0.5 }} />\n      )}\n      {isSpeaking && (\n        // TODO: We'll eventually have separate assets for thinking and speech animations.\n        <Text x={18} y={-10} scale={0.8} text={'💬'} anchor={{ x: 0.5, y: 0.5 }} />\n      )}\n      {isViewer && <ViewerIndicator />}\n      <AnimatedSprite\n        ref={ref}\n        isPlaying={isMoving}\n        textures={spriteSheet.animations[direction]}\n        animationSpeed={speed}\n        anchor={{ x: 0.5, y: 0.5 }}\n      />\n      {emoji && (\n        <Text x={0} y={-24} scale={{ x: -0.8, y: 0.8 }} text={emoji} anchor={{ x: 0.5, y: 0.5 }} />\n      )}\n    </Container>\n  );\n};\n\nfunction ViewerIndicator() {\n  const draw = useCallback((g: PIXI.Graphics) => {\n    g.clear();\n    g.beginFill(0xffff0b, 0.5);\n    g.drawRoundedRect(-10, 10, 20, 10, 100);\n    g.endFill();\n  }, []);\n\n  return <Graphics draw={draw} />;\n}\n"
  },
  {
    "path": "src/components/ConvexClientProvider.tsx",
    "content": "import { ReactNode } from 'react';\nimport { ConvexReactClient, ConvexProvider } from 'convex/react';\n// import { ConvexProviderWithClerk } from 'convex/react-clerk';\n// import { ClerkProvider, useAuth } from '@clerk/clerk-react';\n\n/**\n * Determines the Convex deployment to use.\n *\n * We perform load balancing on the frontend, by randomly selecting one of the available instances.\n * We use localStorage so that individual users stay on the same instance.\n */\nfunction convexUrl(): string {\n  const url = import.meta.env.VITE_CONVEX_URL as string;\n  if (!url) {\n    throw new Error('Couldn’t find the Convex deployment URL.');\n  }\n  return url;\n}\n\nconst convex = new ConvexReactClient(convexUrl(), { unsavedChangesWarning: false });\n\nexport default function ConvexClientProvider({ children }: { children: ReactNode }) {\n  return (\n    // <ClerkProvider publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY as string}>\n    // <ConvexProviderWithClerk client={convex} useAuth={useAuth}>\n    <ConvexProvider client={convex}>{children}</ConvexProvider>\n    // </ConvexProviderWithClerk>\n    // </ClerkProvider>\n  );\n}\n"
  },
  {
    "path": "src/components/DebugPath.tsx",
    "content": "import { Graphics } from '@pixi/react';\nimport { Graphics as PixiGraphics } from 'pixi.js';\nimport { useCallback } from 'react';\nimport { Doc } from '../../convex/_generated/dataModel';\nimport { Player } from '../../convex/aiTown/player';\nimport { unpackPathComponent } from '../../convex/util/types';\n\nexport function DebugPath({ player, tileDim }: { player: Player; tileDim: number }) {\n  const path = player.pathfinding?.state.kind == 'moving' && player.pathfinding.state.path;\n  const draw = useCallback(\n    (g: PixiGraphics) => {\n      g.clear();\n      if (!path) {\n        return;\n      }\n      let first = true;\n      for (const p of path) {\n        const { position } = unpackPathComponent(p as any);\n        const x = position.x * tileDim + tileDim / 2;\n        const y = position.y * tileDim + tileDim / 2;\n        if (first) {\n          g.moveTo(x, y);\n          g.lineStyle(2, debugColor(player.id), 0.5);\n          first = false;\n        } else {\n          g.lineTo(x, y);\n        }\n      }\n    },\n    [path],\n  );\n  return path ? <Graphics draw={draw} /> : null;\n}\nfunction debugColor(_id: string) {\n  return { h: 0, s: 50, l: 90 };\n}\n"
  },
  {
    "path": "src/components/DebugTimeManager.tsx",
    "content": "import { HistoricalTimeManager } from '@/hooks/useHistoricalTime';\nimport { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport uPlot, { AlignedData, Options } from 'uplot';\n\nconst MAX_DATA_POINTS = 10000;\n\nexport function DebugTimeManager(props: {\n  timeManager: HistoricalTimeManager;\n  width: number;\n  height: number;\n}) {\n  const [plotElement, setPlotElement] = useState<HTMLDivElement | null>(null);\n  const [plot, setPlot] = useState<uPlot>();\n\n  useLayoutEffect(() => {\n    if (!plotElement) {\n      return;\n    }\n    const opts: Options = {\n      width: props.width,\n      height: props.height,\n      series: [\n        {},\n        {\n          stroke: 'white',\n          spanGaps: true,\n          pxAlign: 0,\n          points: { show: false },\n          label: 'Buffer health',\n        },\n      ],\n      scales: {\n        y: { distr: 1 },\n      },\n      axes: [\n        {\n          side: 0,\n          show: false,\n        },\n        {\n          ticks: { size: 0 },\n          side: 1,\n          stroke: 'white',\n        },\n      ],\n      legend: {\n        show: false,\n      },\n    };\n    const data: AlignedData = [[], []];\n    const plot = new uPlot(opts, data, plotElement);\n    setPlot(plot);\n  }, [plotElement, props.width, props.height]);\n\n  const timeManager = props.timeManager;\n  const [intervals, setIntervals] = useState([...timeManager.intervals]);\n  useEffect(() => {\n    let reqId: ReturnType<typeof requestAnimationFrame> = 0;\n    const data = {\n      t: [] as number[],\n      bufferHealth: [] as number[],\n    };\n    const update = () => {\n      if (plot) {\n        if (data.t.length > MAX_DATA_POINTS) {\n          data.t = data.t.slice(-MAX_DATA_POINTS);\n          data.bufferHealth = data.bufferHealth.slice(-MAX_DATA_POINTS);\n        }\n        const now = Date.now() / 1000;\n        data.t.push(now);\n        data.bufferHealth.push(timeManager.bufferHealth());\n        setIntervals([...timeManager.intervals]);\n        plot.setData([data.t, data.bufferHealth], true);\n        plot.setScale('x', { min: now - 10, max: now });\n      }\n      reqId = requestAnimationFrame(update);\n    };\n    update();\n    return () => cancelAnimationFrame(reqId);\n  }, [plot, timeManager]);\n\n  let intervalNode: React.ReactNode | null = null;\n  if (intervals.length > 0) {\n    const base = intervals[0].startTs;\n    const baseAge = Date.now() - base;\n\n    intervalNode = (\n      <div style={{ fontSize: '12px' }}>\n        {intervals.length} {intervals.length > 1 ? 'intervals' : 'interval'}:\n        <div style={{ fontSize: '9px', height: '48px' }}>\n          <p>Base: {toSeconds(baseAge)}s ago</p>\n          {intervals.map((interval) => {\n            const containsServerTs =\n              timeManager.prevServerTs &&\n              interval.startTs < timeManager.prevServerTs &&\n              timeManager.prevServerTs <= interval.endTs;\n            let serverTs = null;\n            if (containsServerTs) {\n              serverTs = ` (server: ${toSeconds((timeManager.prevServerTs ?? base) - base)})`;\n            }\n            return (\n              <div\n                key={interval.startTs}\n                style={{ paddingRight: '3px', fontWeight: containsServerTs ? 'bold' : '' }}\n              >\n                {toSeconds(interval.startTs - base)} - {toSeconds(interval.endTs - base)}\n                {serverTs}\n              </div>\n            );\n          })}\n        </div>\n      </div>\n    );\n  }\n  let statusNode: React.ReactNode | null = null;\n  if (timeManager.latestEngineStatus) {\n    const status = timeManager.latestEngineStatus;\n    let statusMsg = status.running ? 'Running' : 'Stopped';\n    statusNode = (\n      <div style={{ fontSize: '12px', paddingTop: '8px' }}>\n        <p>Generation number: {status.generationNumber}</p>\n        <p>Input number: {status.processedInputNumber}</p>\n        <p>Status: {statusMsg}</p>\n        <p>Client skew: {toSeconds(timeManager.clockSkew())}s</p>\n      </div>\n    );\n  }\n  timeManager.latestEngineStatus?.generationNumber;\n\n  return (\n    <div\n      style={{\n        background: 'rgb(53, 59, 89)',\n        position: 'fixed',\n        top: '20px',\n        left: '20px',\n        padding: '10px',\n        border: '1px solid rgb(23, 20, 33)',\n        color: 'white',\n        zIndex: 1,\n      }}\n    >\n      <div style={{ height: '20px', width: '100%', textAlign: 'center' }}>Engine stats</div>\n      {statusNode}\n      <div ref={setPlotElement} />\n      {intervalNode}\n    </div>\n  );\n}\n\n// D3's Tableau10\nexport const COLORS = (\n  '4e79a7f28e2ce1575976b7b259a14fedc949af7aa1ff9da79c755fbab0ab'.match(/.{6}/g) as string[]\n).map((x) => `#${x}`);\n\nconst toSeconds = (n: number) => (n / 1000).toFixed(2);\n"
  },
  {
    "path": "src/components/FreezeButton.tsx",
    "content": "import { useMutation, useQuery } from 'convex/react';\nimport { api } from '../../convex/_generated/api';\nimport Button from './buttons/Button';\n\nexport default function FreezeButton() {\n  const stopAllowed = useQuery(api.testing.stopAllowed) ?? false;\n  const defaultWorld = useQuery(api.world.defaultWorldStatus);\n\n  const frozen = defaultWorld?.status === 'stoppedByDeveloper';\n\n  const unfreeze = useMutation(api.testing.resume);\n  const freeze = useMutation(api.testing.stop);\n\n  const flipSwitch = async () => {\n    if (frozen) {\n      console.log('Unfreezing');\n      await unfreeze();\n    } else {\n      console.log('Freezing');\n      await freeze();\n    }\n  };\n\n  return !stopAllowed ? null : (\n    <>\n      <Button\n        onClick={flipSwitch}\n        className=\"hidden lg:block\"\n        title=\"When freezing a world, the agents will take some time to stop what they are doing before they become frozen. \"\n        imgUrl=\"/assets/star.svg\"\n      >\n        {frozen ? 'Unfreeze' : 'Freeze'}\n      </Button>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/components/Game.tsx",
    "content": "import { useRef, useState } from 'react';\nimport PixiGame from './PixiGame.tsx';\n\nimport { useElementSize } from 'usehooks-ts';\nimport { Stage } from '@pixi/react';\nimport { ConvexProvider, useConvex, useQuery } from 'convex/react';\nimport PlayerDetails from './PlayerDetails.tsx';\nimport { api } from '../../convex/_generated/api';\nimport { useWorldHeartbeat } from '../hooks/useWorldHeartbeat.ts';\nimport { useHistoricalTime } from '../hooks/useHistoricalTime.ts';\nimport { DebugTimeManager } from './DebugTimeManager.tsx';\nimport { GameId } from '../../convex/aiTown/ids.ts';\nimport { useServerGame } from '../hooks/serverGame.ts';\n\nexport const SHOW_DEBUG_UI = !!import.meta.env.VITE_SHOW_DEBUG_UI;\n\nexport default function Game() {\n  const convex = useConvex();\n  const [selectedElement, setSelectedElement] = useState<{\n    kind: 'player';\n    id: GameId<'players'>;\n  }>();\n  const [gameWrapperRef, { width, height }] = useElementSize();\n\n  const worldStatus = useQuery(api.world.defaultWorldStatus);\n  const worldId = worldStatus?.worldId;\n  const engineId = worldStatus?.engineId;\n\n  const game = useServerGame(worldId);\n\n  // Send a periodic heartbeat to our world to keep it alive.\n  useWorldHeartbeat();\n\n  const worldState = useQuery(api.world.worldState, worldId ? { worldId } : 'skip');\n  const { historicalTime, timeManager } = useHistoricalTime(worldState?.engine);\n\n  const scrollViewRef = useRef<HTMLDivElement>(null);\n\n  if (!worldId || !engineId || !game) {\n    return null;\n  }\n  return (\n    <>\n      {SHOW_DEBUG_UI && <DebugTimeManager timeManager={timeManager} width={200} height={100} />}\n      <div className=\"mx-auto w-full max-w grid grid-rows-[240px_1fr] lg:grid-rows-[1fr] lg:grid-cols-[1fr_auto] lg:grow max-w-[1400px] min-h-[480px] game-frame\">\n        {/* Game area */}\n        <div className=\"relative overflow-hidden bg-brown-900\" ref={gameWrapperRef}>\n          <div className=\"absolute inset-0\">\n            <div className=\"container\">\n              <Stage width={width} height={height} options={{ backgroundColor: 0x7ab5ff }}>\n                {/* Re-propagate context because contexts are not shared between renderers.\nhttps://github.com/michalochman/react-pixi-fiber/issues/145#issuecomment-531549215 */}\n                <ConvexProvider client={convex}>\n                  <PixiGame\n                    game={game}\n                    worldId={worldId}\n                    engineId={engineId}\n                    width={width}\n                    height={height}\n                    historicalTime={historicalTime}\n                    setSelectedElement={setSelectedElement}\n                  />\n                </ConvexProvider>\n              </Stage>\n            </div>\n          </div>\n        </div>\n        {/* Right column area */}\n        <div\n          className=\"flex flex-col overflow-y-auto shrink-0 px-4 py-6 sm:px-6 lg:w-96 xl:pr-6 border-t-8 sm:border-t-0 sm:border-l-8 border-brown-900  bg-brown-800 text-brown-100\"\n          ref={scrollViewRef}\n        >\n          <PlayerDetails\n            worldId={worldId}\n            engineId={engineId}\n            game={game}\n            playerId={selectedElement?.id}\n            setSelectedElement={setSelectedElement}\n            scrollViewRef={scrollViewRef}\n          />\n        </div>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/components/MessageInput.tsx",
    "content": "import clsx from 'clsx';\nimport { useMutation, useQuery } from 'convex/react';\nimport { KeyboardEvent, useRef, useState } from 'react';\nimport { api } from '../../convex/_generated/api';\nimport { Id } from '../../convex/_generated/dataModel';\nimport { useSendInput } from '../hooks/sendInput';\nimport { Player } from '../../convex/aiTown/player';\nimport { Conversation } from '../../convex/aiTown/conversation';\n\nexport function MessageInput({\n  worldId,\n  engineId,\n  humanPlayer,\n  conversation,\n}: {\n  worldId: Id<'worlds'>;\n  engineId: Id<'engines'>;\n  humanPlayer: Player;\n  conversation: Conversation;\n}) {\n  const descriptions = useQuery(api.world.gameDescriptions, { worldId });\n  const humanName = descriptions?.playerDescriptions.find((p) => p.playerId === humanPlayer.id)\n    ?.name;\n  const inputRef = useRef<HTMLParagraphElement>(null);\n  const inflightUuid = useRef<string | undefined>();\n  const writeMessage = useMutation(api.messages.writeMessage);\n  const startTyping = useSendInput(engineId, 'startTyping');\n  const currentlyTyping = conversation.isTyping;\n\n  const onKeyDown = async (e: KeyboardEvent) => {\n    e.stopPropagation();\n\n    // Set the typing indicator if we're not submitting.\n    if (e.key !== 'Enter') {\n      console.log(inflightUuid.current);\n      if (currentlyTyping || inflightUuid.current !== undefined) {\n        return;\n      }\n      inflightUuid.current = crypto.randomUUID();\n      try {\n        // Don't show a toast on error.\n        await startTyping({\n          playerId: humanPlayer.id,\n          conversationId: conversation.id,\n          messageUuid: inflightUuid.current,\n        });\n      } finally {\n        inflightUuid.current = undefined;\n      }\n      return;\n    }\n\n    // Send the current message.\n    e.preventDefault();\n    if (!inputRef.current) {\n      return;\n    }\n    const text = inputRef.current.innerText;\n    inputRef.current.innerText = '';\n    if (!text) {\n      return;\n    }\n    let messageUuid = inflightUuid.current;\n    if (currentlyTyping && currentlyTyping.playerId === humanPlayer.id) {\n      messageUuid = currentlyTyping.messageUuid;\n    }\n    messageUuid = messageUuid || crypto.randomUUID();\n    await writeMessage({\n      worldId,\n      playerId: humanPlayer.id,\n      conversationId: conversation.id,\n      text,\n      messageUuid,\n    });\n  };\n  return (\n    <div className=\"leading-tight mb-6\">\n      <div className=\"flex gap-4\">\n        <span className=\"uppercase flex-grow\">{humanName}</span>\n      </div>\n      <div className={clsx('bubble', 'bubble-mine')}>\n        <p\n          className=\"bg-white -mx-3 -my-1\"\n          ref={inputRef}\n          contentEditable\n          style={{ outline: 'none' }}\n          tabIndex={0}\n          placeholder=\"Type here\"\n          onKeyDown={(e) => onKeyDown(e)}\n        />\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/Messages.tsx",
    "content": "import clsx from 'clsx';\nimport { Doc, Id } from '../../convex/_generated/dataModel';\nimport { useQuery } from 'convex/react';\nimport { api } from '../../convex/_generated/api';\nimport { MessageInput } from './MessageInput';\nimport { Player } from '../../convex/aiTown/player';\nimport { Conversation } from '../../convex/aiTown/conversation';\nimport { useEffect, useRef } from 'react';\n\nexport function Messages({\n  worldId,\n  engineId,\n  conversation,\n  inConversationWithMe,\n  humanPlayer,\n  scrollViewRef,\n}: {\n  worldId: Id<'worlds'>;\n  engineId: Id<'engines'>;\n  conversation:\n    | { kind: 'active'; doc: Conversation }\n    | { kind: 'archived'; doc: Doc<'archivedConversations'> };\n  inConversationWithMe: boolean;\n  humanPlayer?: Player;\n  scrollViewRef: React.RefObject<HTMLDivElement>;\n}) {\n  const humanPlayerId = humanPlayer?.id;\n  const descriptions = useQuery(api.world.gameDescriptions, { worldId });\n  const messages = useQuery(api.messages.listMessages, {\n    worldId,\n    conversationId: conversation.doc.id,\n  });\n  let currentlyTyping = conversation.kind === 'active' ? conversation.doc.isTyping : undefined;\n  if (messages !== undefined && currentlyTyping) {\n    if (messages.find((m) => m.messageUuid === currentlyTyping!.messageUuid)) {\n      currentlyTyping = undefined;\n    }\n  }\n  const currentlyTypingName =\n    currentlyTyping &&\n    descriptions?.playerDescriptions.find((p) => p.playerId === currentlyTyping?.playerId)?.name;\n\n  const scrollView = scrollViewRef.current;\n  const isScrolledToBottom = useRef(false);\n  useEffect(() => {\n    if (!scrollView) return undefined;\n\n    const onScroll = () => {\n      isScrolledToBottom.current = !!(\n        scrollView && scrollView.scrollHeight - scrollView.scrollTop - 50 <= scrollView.clientHeight\n      );\n    };\n    scrollView.addEventListener('scroll', onScroll);\n    return () => scrollView.removeEventListener('scroll', onScroll);\n  }, [scrollView]);\n  useEffect(() => {\n    if (isScrolledToBottom.current) {\n      scrollViewRef.current?.scrollTo({\n        top: scrollViewRef.current.scrollHeight,\n        behavior: 'smooth',\n      });\n    }\n  }, [messages, currentlyTyping]);\n\n  if (messages === undefined) {\n    return null;\n  }\n  if (messages.length === 0 && !inConversationWithMe) {\n    return null;\n  }\n  const messageNodes: { time: number; node: React.ReactNode }[] = messages.map((m) => {\n    const node = (\n      <div key={`text-${m._id}`} className=\"leading-tight mb-6\">\n        <div className=\"flex gap-4\">\n          <span className=\"uppercase flex-grow\">{m.authorName}</span>\n          <time dateTime={m._creationTime.toString()}>\n            {new Date(m._creationTime).toLocaleString()}\n          </time>\n        </div>\n        <div className={clsx('bubble', m.author === humanPlayerId && 'bubble-mine')}>\n          <p className=\"bg-white -mx-3 -my-1\">{m.text}</p>\n        </div>\n      </div>\n    );\n    return { node, time: m._creationTime };\n  });\n  const lastMessageTs = messages.map((m) => m._creationTime).reduce((a, b) => Math.max(a, b), 0);\n\n  const membershipNodes: typeof messageNodes = [];\n  if (conversation.kind === 'active') {\n    for (const [playerId, m] of conversation.doc.participants) {\n      const playerName = descriptions?.playerDescriptions.find((p) => p.playerId === playerId)\n        ?.name;\n      let started;\n      if (m.status.kind === 'participating') {\n        started = m.status.started;\n      }\n      if (started) {\n        membershipNodes.push({\n          node: (\n            <div key={`joined-${playerId}`} className=\"leading-tight mb-6\">\n              <p className=\"text-brown-700 text-center\">{playerName} joined the conversation.</p>\n            </div>\n          ),\n          time: started,\n        });\n      }\n    }\n  } else {\n    for (const playerId of conversation.doc.participants) {\n      const playerName = descriptions?.playerDescriptions.find((p) => p.playerId === playerId)\n        ?.name;\n      const started = conversation.doc.created;\n      membershipNodes.push({\n        node: (\n          <div key={`joined-${playerId}`} className=\"leading-tight mb-6\">\n            <p className=\"text-brown-700 text-center\">{playerName} joined the conversation.</p>\n          </div>\n        ),\n        time: started,\n      });\n      const ended = conversation.doc.ended;\n      membershipNodes.push({\n        node: (\n          <div key={`left-${playerId}`} className=\"leading-tight mb-6\">\n            <p className=\"text-brown-700 text-center\">{playerName} left the conversation.</p>\n          </div>\n        ),\n        // Always sort all \"left\" messages after the last message.\n        // TODO: We can remove this once we want to support more than two participants per conversation.\n        time: Math.max(lastMessageTs + 1, ended),\n      });\n    }\n  }\n  const nodes = [...messageNodes, ...membershipNodes];\n  nodes.sort((a, b) => a.time - b.time);\n  return (\n    <div className=\"chats text-base sm:text-sm\">\n      <div className=\"bg-brown-200 text-black p-2\">\n        {nodes.length > 0 && nodes.map((n) => n.node)}\n        {currentlyTyping && currentlyTyping.playerId !== humanPlayerId && (\n          <div key=\"typing\" className=\"leading-tight mb-6\">\n            <div className=\"flex gap-4\">\n              <span className=\"uppercase flex-grow\">{currentlyTypingName}</span>\n              <time dateTime={currentlyTyping.since.toString()}>\n                {new Date(currentlyTyping.since).toLocaleString()}\n              </time>\n            </div>\n            <div className={clsx('bubble')}>\n              <p className=\"bg-white -mx-3 -my-1\">\n                <i>typing...</i>\n              </p>\n            </div>\n          </div>\n        )}\n        {humanPlayer && inConversationWithMe && conversation.kind === 'active' && (\n          <MessageInput\n            worldId={worldId}\n            engineId={engineId}\n            conversation={conversation.doc}\n            humanPlayer={humanPlayer}\n          />\n        )}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/PixiGame.tsx",
    "content": "import * as PIXI from 'pixi.js';\nimport { useApp } from '@pixi/react';\nimport { Player, SelectElement } from './Player.tsx';\nimport { useEffect, useRef, useState } from 'react';\nimport { PixiStaticMap } from './PixiStaticMap.tsx';\nimport PixiViewport from './PixiViewport.tsx';\nimport { Viewport } from 'pixi-viewport';\nimport { Id } from '../../convex/_generated/dataModel';\nimport { useQuery } from 'convex/react';\nimport { api } from '../../convex/_generated/api.js';\nimport { useSendInput } from '../hooks/sendInput.ts';\nimport { toastOnError } from '../toasts.ts';\nimport { DebugPath } from './DebugPath.tsx';\nimport { PositionIndicator } from './PositionIndicator.tsx';\nimport { SHOW_DEBUG_UI } from './Game.tsx';\nimport { ServerGame } from '../hooks/serverGame.ts';\n\nexport const PixiGame = (props: {\n  worldId: Id<'worlds'>;\n  engineId: Id<'engines'>;\n  game: ServerGame;\n  historicalTime: number | undefined;\n  width: number;\n  height: number;\n  setSelectedElement: SelectElement;\n}) => {\n  // PIXI setup.\n  const pixiApp = useApp();\n  const viewportRef = useRef<Viewport | undefined>();\n\n  const humanTokenIdentifier = useQuery(api.world.userStatus, { worldId: props.worldId }) ?? null;\n  const humanPlayerId = [...props.game.world.players.values()].find(\n    (p) => p.human === humanTokenIdentifier,\n  )?.id;\n\n  const moveTo = useSendInput(props.engineId, 'moveTo');\n\n  // Interaction for clicking on the world to navigate.\n  const dragStart = useRef<{ screenX: number; screenY: number } | null>(null);\n  const onMapPointerDown = (e: any) => {\n    // https://pixijs.download/dev/docs/PIXI.FederatedPointerEvent.html\n    dragStart.current = { screenX: e.screenX, screenY: e.screenY };\n  };\n\n  const [lastDestination, setLastDestination] = useState<{\n    x: number;\n    y: number;\n    t: number;\n  } | null>(null);\n  const onMapPointerUp = async (e: any) => {\n    if (dragStart.current) {\n      const { screenX, screenY } = dragStart.current;\n      dragStart.current = null;\n      const [dx, dy] = [screenX - e.screenX, screenY - e.screenY];\n      const dist = Math.sqrt(dx * dx + dy * dy);\n      if (dist > 10) {\n        console.log(`Skipping navigation on drag event (${dist}px)`);\n        return;\n      }\n    }\n    if (!humanPlayerId) {\n      return;\n    }\n    const viewport = viewportRef.current;\n    if (!viewport) {\n      return;\n    }\n    const gameSpacePx = viewport.toWorld(e.screenX, e.screenY);\n    const tileDim = props.game.worldMap.tileDim;\n    const gameSpaceTiles = {\n      x: gameSpacePx.x / tileDim,\n      y: gameSpacePx.y / tileDim,\n    };\n    setLastDestination({ t: Date.now(), ...gameSpaceTiles });\n    const roundedTiles = {\n      x: Math.floor(gameSpaceTiles.x),\n      y: Math.floor(gameSpaceTiles.y),\n    };\n    console.log(`Moving to ${JSON.stringify(roundedTiles)}`);\n    await toastOnError(moveTo({ playerId: humanPlayerId, destination: roundedTiles }));\n  };\n  const { width, height, tileDim } = props.game.worldMap;\n  const players = [...props.game.world.players.values()];\n\n  // Zoom on the user’s avatar when it is created\n  useEffect(() => {\n    if (!viewportRef.current || humanPlayerId === undefined) return;\n\n    const humanPlayer = props.game.world.players.get(humanPlayerId)!;\n    viewportRef.current.animate({\n      position: new PIXI.Point(humanPlayer.position.x * tileDim, humanPlayer.position.y * tileDim),\n      scale: 1.5,\n    });\n  }, [humanPlayerId]);\n\n  return (\n    <PixiViewport\n      app={pixiApp}\n      screenWidth={props.width}\n      screenHeight={props.height}\n      worldWidth={width * tileDim}\n      worldHeight={height * tileDim}\n      viewportRef={viewportRef}\n    >\n      <PixiStaticMap\n        map={props.game.worldMap}\n        onpointerup={onMapPointerUp}\n        onpointerdown={onMapPointerDown}\n      />\n      {players.map(\n        (p) =>\n          // Only show the path for the human player in non-debug mode.\n          (SHOW_DEBUG_UI || p.id === humanPlayerId) && (\n            <DebugPath key={`path-${p.id}`} player={p} tileDim={tileDim} />\n          ),\n      )}\n      {lastDestination && <PositionIndicator destination={lastDestination} tileDim={tileDim} />}\n      {players.map((p) => (\n        <Player\n          key={`player-${p.id}`}\n          game={props.game}\n          player={p}\n          isViewer={p.id === humanPlayerId}\n          onClick={props.setSelectedElement}\n          historicalTime={props.historicalTime}\n        />\n      ))}\n    </PixiViewport>\n  );\n};\nexport default PixiGame;\n"
  },
  {
    "path": "src/components/PixiStaticMap.tsx",
    "content": "import { PixiComponent, applyDefaultProps } from '@pixi/react';\nimport * as PIXI from 'pixi.js';\nimport { AnimatedSprite, WorldMap } from '../../convex/aiTown/worldMap';\nimport * as campfire from '../../data/animations/campfire.json';\nimport * as gentlesparkle from '../../data/animations/gentlesparkle.json';\nimport * as gentlewaterfall from '../../data/animations/gentlewaterfall.json';\nimport * as gentlesplash from '../../data/animations/gentlesplash.json';\nimport * as windmill from '../../data/animations/windmill.json';\n\nconst animations = {\n  'campfire.json': { spritesheet: campfire, url: '/ai-town/assets/spritesheets/campfire.png' },\n  'gentlesparkle.json': {\n    spritesheet: gentlesparkle,\n    url: '/ai-town/assets/spritesheets/gentlesparkle32.png',\n  },\n  'gentlewaterfall.json': {\n    spritesheet: gentlewaterfall,\n    url: '/ai-town/assets/spritesheets/gentlewaterfall32.png',\n  },\n  'windmill.json': { spritesheet: windmill, url: '/ai-town/assets/spritesheets/windmill.png' },\n  'gentlesplash.json': { spritesheet: gentlesplash,\n    url: '/ai-town/assets/spritesheets/gentlewaterfall32.png',},\n};\n\nexport const PixiStaticMap = PixiComponent('StaticMap', {\n  create: (props: { map: WorldMap; [k: string]: any }) => {\n    const map = props.map;\n    const numxtiles = Math.floor(map.tileSetDimX / map.tileDim);\n    const numytiles = Math.floor(map.tileSetDimY / map.tileDim);\n    const bt = PIXI.BaseTexture.from(map.tileSetUrl, {\n      scaleMode: PIXI.SCALE_MODES.NEAREST,\n    });\n\n    const tiles = [];\n    for (let x = 0; x < numxtiles; x++) {\n      for (let y = 0; y < numytiles; y++) {\n        tiles[x + y * numxtiles] = new PIXI.Texture(\n          bt,\n          new PIXI.Rectangle(x * map.tileDim, y * map.tileDim, map.tileDim, map.tileDim),\n        );\n      }\n    }\n    const screenxtiles = map.bgTiles[0].length;\n    const screenytiles = map.bgTiles[0][0].length;\n\n    const container = new PIXI.Container();\n    const allLayers = [...map.bgTiles, ...map.objectTiles];\n\n    // blit bg & object layers of map onto canvas\n    for (let i = 0; i < screenxtiles * screenytiles; i++) {\n      const x = i % screenxtiles;\n      const y = Math.floor(i / screenxtiles);\n      const xPx = x * map.tileDim;\n      const yPx = y * map.tileDim;\n\n      // Add all layers of backgrounds.\n      for (const layer of allLayers) {\n        const tileIndex = layer[x][y];\n        // Some layers may not have tiles at this location.\n        if (tileIndex === -1) continue;\n        const ctile = new PIXI.Sprite(tiles[tileIndex]);\n        ctile.x = xPx;\n        ctile.y = yPx;\n        container.addChild(ctile);\n      }\n    }\n\n    // TODO: Add layers.\n    const spritesBySheet = new Map<string, AnimatedSprite[]>();\n    for (const sprite of map.animatedSprites) {\n      const sheet = sprite.sheet;\n      if (!spritesBySheet.has(sheet)) {\n        spritesBySheet.set(sheet, []);\n      }\n      spritesBySheet.get(sheet)!.push(sprite);\n    }\n    for (const [sheet, sprites] of spritesBySheet.entries()) {\n      const animation = (animations as any)[sheet];\n      if (!animation) {\n        console.error('Could not find animation', sheet);\n        continue;\n      }\n      const { spritesheet, url } = animation;\n      const texture = PIXI.BaseTexture.from(url, {\n        scaleMode: PIXI.SCALE_MODES.NEAREST,\n      });\n      const spriteSheet = new PIXI.Spritesheet(texture, spritesheet);\n      spriteSheet.parse().then(() => {\n        for (const sprite of sprites) {\n          const pixiAnimation = spriteSheet.animations[sprite.animation];\n          if (!pixiAnimation) {\n            console.error('Failed to load animation', sprite);\n            continue;\n          }\n          const pixiSprite = new PIXI.AnimatedSprite(pixiAnimation);\n          pixiSprite.animationSpeed = 0.1;\n          pixiSprite.autoUpdate = true;\n          pixiSprite.x = sprite.x;\n          pixiSprite.y = sprite.y;\n          pixiSprite.width = sprite.w;\n          pixiSprite.height = sprite.h;\n          container.addChild(pixiSprite);\n          pixiSprite.play();\n        }\n      });\n    }\n\n    container.x = 0;\n    container.y = 0;\n\n    // Set the hit area manually to ensure `pointerdown` events are delivered to this container.\n    container.interactive = true;\n    container.hitArea = new PIXI.Rectangle(\n      0,\n      0,\n      screenxtiles * map.tileDim,\n      screenytiles * map.tileDim,\n    );\n\n    return container;\n  },\n\n  applyProps: (instance, oldProps, newProps) => {\n    applyDefaultProps(instance, oldProps, newProps);\n  },\n});\n"
  },
  {
    "path": "src/components/PixiViewport.tsx",
    "content": "// Based on https://codepen.io/inlet/pen/yLVmPWv.\n// Copyright (c) 2018 Patrick Brouwer, distributed under the MIT license.\n\nimport { PixiComponent, useApp } from '@pixi/react';\nimport { Viewport } from 'pixi-viewport';\nimport { Application } from 'pixi.js';\nimport { MutableRefObject, ReactNode } from 'react';\n\nexport type ViewportProps = {\n  app: Application;\n  viewportRef?: MutableRefObject<Viewport | undefined>;\n\n  screenWidth: number;\n  screenHeight: number;\n  worldWidth: number;\n  worldHeight: number;\n  children?: ReactNode;\n};\n\n// https://davidfig.github.io/pixi-viewport/jsdoc/Viewport.html\nexport default PixiComponent('Viewport', {\n  create(props: ViewportProps) {\n    const { app, children, viewportRef, ...viewportProps } = props;\n    const viewport = new Viewport({\n      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n      events: app.renderer.events,\n      passiveWheel: false,\n      ...viewportProps,\n    });\n    if (viewportRef) {\n      viewportRef.current = viewport;\n    }\n    // Activate plugins\n    viewport\n      .drag()\n      .pinch({})\n      .wheel()\n      .decelerate()\n      .clamp({ direction: 'all', underflow: 'center' })\n      .setZoom(-10)\n      .clampZoom({\n        minScale: (1.04 * props.screenWidth) / (props.worldWidth / 2),\n        maxScale: 3.0,\n      });\n    return viewport;\n  },\n  applyProps(viewport, oldProps: any, newProps: any) {\n    Object.keys(newProps).forEach((p) => {\n      if (p !== 'app' && p !== 'viewportRef' && p !== 'children' && oldProps[p] !== newProps[p]) {\n        // @ts-expect-error Ignoring TypeScript here\n        // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n        viewport[p] = newProps[p];\n      }\n    });\n  },\n});\n"
  },
  {
    "path": "src/components/Player.tsx",
    "content": "import { Character } from './Character.tsx';\nimport { orientationDegrees } from '../../convex/util/geometry.ts';\nimport { characters } from '../../data/characters.ts';\nimport { toast } from 'react-toastify';\nimport { Player as ServerPlayer } from '../../convex/aiTown/player.ts';\nimport { GameId } from '../../convex/aiTown/ids.ts';\nimport { Id } from '../../convex/_generated/dataModel';\nimport { Location, locationFields, playerLocation } from '../../convex/aiTown/location.ts';\nimport { useHistoricalValue } from '../hooks/useHistoricalValue.ts';\nimport { PlayerDescription } from '../../convex/aiTown/playerDescription.ts';\nimport { WorldMap } from '../../convex/aiTown/worldMap.ts';\nimport { ServerGame } from '../hooks/serverGame.ts';\n\nexport type SelectElement = (element?: { kind: 'player'; id: GameId<'players'> }) => void;\n\nconst logged = new Set<string>();\n\nexport const Player = ({\n  game,\n  isViewer,\n  player,\n  onClick,\n  historicalTime,\n}: {\n  game: ServerGame;\n  isViewer: boolean;\n  player: ServerPlayer;\n\n  onClick: SelectElement;\n  historicalTime?: number;\n}) => {\n  const playerCharacter = game.playerDescriptions.get(player.id)?.character;\n  if (!playerCharacter) {\n    throw new Error(`Player ${player.id} has no character`);\n  }\n  const character = characters.find((c) => c.name === playerCharacter);\n\n  const locationBuffer = game.world.historicalLocations?.get(player.id);\n  const historicalLocation = useHistoricalValue<Location>(\n    locationFields,\n    historicalTime,\n    playerLocation(player),\n    locationBuffer,\n  );\n  if (!character) {\n    if (!logged.has(playerCharacter)) {\n      logged.add(playerCharacter);\n      toast.error(`Unknown character ${playerCharacter}`);\n    }\n    return null;\n  }\n\n  if (!historicalLocation) {\n    return null;\n  }\n\n  const isSpeaking = !![...game.world.conversations.values()].find(\n    (c) => c.isTyping?.playerId === player.id,\n  );\n  const isThinking =\n    !isSpeaking &&\n    !![...game.world.agents.values()].find(\n      (a) => a.playerId === player.id && !!a.inProgressOperation,\n    );\n  const tileDim = game.worldMap.tileDim;\n  const historicalFacing = { dx: historicalLocation.dx, dy: historicalLocation.dy };\n  return (\n    <>\n      <Character\n        x={historicalLocation.x * tileDim + tileDim / 2}\n        y={historicalLocation.y * tileDim + tileDim / 2}\n        orientation={orientationDegrees(historicalFacing)}\n        isMoving={historicalLocation.speed > 0}\n        isThinking={isThinking}\n        isSpeaking={isSpeaking}\n        emoji={\n          player.activity && player.activity.until > (historicalTime ?? Date.now())\n            ? player.activity?.emoji\n            : undefined\n        }\n        isViewer={isViewer}\n        textureUrl={character.textureUrl}\n        spritesheetData={character.spritesheetData}\n        speed={character.speed}\n        onClick={() => {\n          onClick({ kind: 'player', id: player.id });\n        }}\n      />\n    </>\n  );\n};\n"
  },
  {
    "path": "src/components/PlayerDetails.tsx",
    "content": "import { useQuery } from 'convex/react';\nimport { api } from '../../convex/_generated/api';\nimport { Id } from '../../convex/_generated/dataModel';\nimport closeImg from '../../assets/close.svg';\nimport { SelectElement } from './Player';\nimport { Messages } from './Messages';\nimport { toastOnError } from '../toasts';\nimport { useSendInput } from '../hooks/sendInput';\nimport { Player } from '../../convex/aiTown/player';\nimport { GameId } from '../../convex/aiTown/ids';\nimport { ServerGame } from '../hooks/serverGame';\n\nexport default function PlayerDetails({\n  worldId,\n  engineId,\n  game,\n  playerId,\n  setSelectedElement,\n  scrollViewRef,\n}: {\n  worldId: Id<'worlds'>;\n  engineId: Id<'engines'>;\n  game: ServerGame;\n  playerId?: GameId<'players'>;\n  setSelectedElement: SelectElement;\n  scrollViewRef: React.RefObject<HTMLDivElement>;\n}) {\n  const humanTokenIdentifier = useQuery(api.world.userStatus, { worldId });\n\n  const players = [...game.world.players.values()];\n  const humanPlayer = players.find((p) => p.human === humanTokenIdentifier);\n  const humanConversation = humanPlayer ? game.world.playerConversation(humanPlayer) : undefined;\n  // Always select the other player if we're in a conversation with them.\n  if (humanPlayer && humanConversation) {\n    const otherPlayerIds = [...humanConversation.participants.keys()].filter(\n      (p) => p !== humanPlayer.id,\n    );\n    playerId = otherPlayerIds[0];\n  }\n\n  const player = playerId && game.world.players.get(playerId);\n  const playerConversation = player && game.world.playerConversation(player);\n\n  const previousConversation = useQuery(\n    api.world.previousConversation,\n    playerId ? { worldId, playerId } : 'skip',\n  );\n\n  const playerDescription = playerId && game.playerDescriptions.get(playerId);\n\n  const startConversation = useSendInput(engineId, 'startConversation');\n  const acceptInvite = useSendInput(engineId, 'acceptInvite');\n  const rejectInvite = useSendInput(engineId, 'rejectInvite');\n  const leaveConversation = useSendInput(engineId, 'leaveConversation');\n\n  if (!playerId) {\n    return (\n      <div className=\"h-full text-xl flex text-center items-center p-4\">\n        Click on an agent on the map to see chat history.\n      </div>\n    );\n  }\n  if (!player) {\n    return null;\n  }\n  const isMe = humanPlayer && player.id === humanPlayer.id;\n  const canInvite = !isMe && !playerConversation && humanPlayer && !humanConversation;\n  const sameConversation =\n    !isMe &&\n    humanPlayer &&\n    humanConversation &&\n    playerConversation &&\n    humanConversation.id === playerConversation.id;\n\n  const humanStatus =\n    humanPlayer && humanConversation && humanConversation.participants.get(humanPlayer.id)?.status;\n  const playerStatus = playerConversation && playerConversation.participants.get(playerId)?.status;\n\n  const haveInvite = sameConversation && humanStatus?.kind === 'invited';\n  const waitingForAccept =\n    sameConversation && playerConversation.participants.get(playerId)?.status.kind === 'invited';\n  const waitingForNearby =\n    sameConversation && playerStatus?.kind === 'walkingOver' && humanStatus?.kind === 'walkingOver';\n\n  const inConversationWithMe =\n    sameConversation &&\n    playerStatus?.kind === 'participating' &&\n    humanStatus?.kind === 'participating';\n\n  const onStartConversation = async () => {\n    if (!humanPlayer || !playerId) {\n      return;\n    }\n    console.log(`Starting conversation`);\n    await toastOnError(startConversation({ playerId: humanPlayer.id, invitee: playerId }));\n  };\n  const onAcceptInvite = async () => {\n    if (!humanPlayer || !humanConversation || !playerId) {\n      return;\n    }\n    await toastOnError(\n      acceptInvite({\n        playerId: humanPlayer.id,\n        conversationId: humanConversation.id,\n      }),\n    );\n  };\n  const onRejectInvite = async () => {\n    if (!humanPlayer || !humanConversation) {\n      return;\n    }\n    await toastOnError(\n      rejectInvite({\n        playerId: humanPlayer.id,\n        conversationId: humanConversation.id,\n      }),\n    );\n  };\n  const onLeaveConversation = async () => {\n    if (!humanPlayer || !inConversationWithMe || !humanConversation) {\n      return;\n    }\n    await toastOnError(\n      leaveConversation({\n        playerId: humanPlayer.id,\n        conversationId: humanConversation.id,\n      }),\n    );\n  };\n  // const pendingSuffix = (inputName: string) =>\n  //   [...inflightInputs.values()].find((i) => i.name === inputName) ? ' opacity-50' : '';\n\n  const pendingSuffix = (s: string) => '';\n  return (\n    <>\n      <div className=\"flex gap-4\">\n        <div className=\"box w-3/4 sm:w-full mr-auto\">\n          <h2 className=\"bg-brown-700 p-2 font-display text-2xl sm:text-4xl tracking-wider shadow-solid text-center\">\n            {playerDescription?.name}\n          </h2>\n        </div>\n        <a\n          className=\"button text-white shadow-solid text-2xl cursor-pointer pointer-events-auto\"\n          onClick={() => setSelectedElement(undefined)}\n        >\n          <h2 className=\"h-full bg-clay-700\">\n            <img className=\"w-4 h-4 sm:w-5 sm:h-5\" src={closeImg} />\n          </h2>\n        </a>\n      </div>\n      {canInvite && (\n        <a\n          className={\n            'mt-6 button text-white shadow-solid text-xl cursor-pointer pointer-events-auto' +\n            pendingSuffix('startConversation')\n          }\n          onClick={onStartConversation}\n        >\n          <div className=\"h-full bg-clay-700 text-center\">\n            <span>Start conversation</span>\n          </div>\n        </a>\n      )}\n      {waitingForAccept && (\n        <a className=\"mt-6 button text-white shadow-solid text-xl cursor-pointer pointer-events-auto opacity-50\">\n          <div className=\"h-full bg-clay-700 text-center\">\n            <span>Waiting for accept...</span>\n          </div>\n        </a>\n      )}\n      {waitingForNearby && (\n        <a className=\"mt-6 button text-white shadow-solid text-xl cursor-pointer pointer-events-auto opacity-50\">\n          <div className=\"h-full bg-clay-700 text-center\">\n            <span>Walking over...</span>\n          </div>\n        </a>\n      )}\n      {inConversationWithMe && (\n        <a\n          className={\n            'mt-6 button text-white shadow-solid text-xl cursor-pointer pointer-events-auto' +\n            pendingSuffix('leaveConversation')\n          }\n          onClick={onLeaveConversation}\n        >\n          <div className=\"h-full bg-clay-700 text-center\">\n            <span>Leave conversation</span>\n          </div>\n        </a>\n      )}\n      {haveInvite && (\n        <>\n          <a\n            className={\n              'mt-6 button text-white shadow-solid text-xl cursor-pointer pointer-events-auto' +\n              pendingSuffix('acceptInvite')\n            }\n            onClick={onAcceptInvite}\n          >\n            <div className=\"h-full bg-clay-700 text-center\">\n              <span>Accept</span>\n            </div>\n          </a>\n          <a\n            className={\n              'mt-6 button text-white shadow-solid text-xl cursor-pointer pointer-events-auto' +\n              pendingSuffix('rejectInvite')\n            }\n            onClick={onRejectInvite}\n          >\n            <div className=\"h-full bg-clay-700 text-center\">\n              <span>Reject</span>\n            </div>\n          </a>\n        </>\n      )}\n      {!playerConversation && player.activity && player.activity.until > Date.now() && (\n        <div className=\"box flex-grow mt-6\">\n          <h2 className=\"bg-brown-700 text-base sm:text-lg text-center\">\n            {player.activity.description}\n          </h2>\n        </div>\n      )}\n      <div className=\"desc my-6\">\n        <p className=\"leading-tight -m-4 bg-brown-700 text-base sm:text-sm\">\n          {!isMe && playerDescription?.description}\n          {isMe && <i>This is you!</i>}\n          {!isMe && inConversationWithMe && (\n            <>\n              <br />\n              <br />(<i>Conversing with you!</i>)\n            </>\n          )}\n        </p>\n      </div>\n      {!isMe && playerConversation && playerStatus?.kind === 'participating' && (\n        <Messages\n          worldId={worldId}\n          engineId={engineId}\n          inConversationWithMe={inConversationWithMe ?? false}\n          conversation={{ kind: 'active', doc: playerConversation }}\n          humanPlayer={humanPlayer}\n          scrollViewRef={scrollViewRef}\n        />\n      )}\n      {!playerConversation && previousConversation && (\n        <>\n          <div className=\"box flex-grow\">\n            <h2 className=\"bg-brown-700 text-lg text-center\">Previous conversation</h2>\n          </div>\n          <Messages\n            worldId={worldId}\n            engineId={engineId}\n            inConversationWithMe={false}\n            conversation={{ kind: 'archived', doc: previousConversation }}\n            humanPlayer={humanPlayer}\n            scrollViewRef={scrollViewRef}\n          />\n        </>\n      )}\n    </>\n  );\n}\n"
  },
  {
    "path": "src/components/PositionIndicator.tsx",
    "content": "import { useCallback, useState } from 'react';\nimport { Graphics } from '@pixi/react';\nimport { Graphics as PixiGraphics } from 'pixi.js';\n\nconst ANIMATION_DURATION = 500;\nconst RADIUS_TILES = 0.25;\n\nexport function PositionIndicator(props: {\n  destination: { x: number; y: number; t: number };\n  tileDim: number;\n}) {\n  const { destination, tileDim } = props;\n  const draw = (g: PixiGraphics) => {\n    g.clear();\n    const now = Date.now();\n    if (destination.t + ANIMATION_DURATION <= now) {\n      return;\n    }\n    const progress = (now - destination.t) / ANIMATION_DURATION;\n    const x = destination.x * tileDim;\n    const y = destination.y * tileDim;\n    g.lineStyle(1.5, { h: 0, s: 50, l: 90 }, 0.5);\n    g.drawCircle(x, y, RADIUS_TILES * progress * tileDim);\n  };\n  return <Graphics draw={draw} />;\n}\n"
  },
  {
    "path": "src/components/PoweredByConvex.tsx",
    "content": "import bannerBg from '../../assets/convex-bg.webp';\nexport default function PoweredByConvex() {\n  return (\n    <a\n      href=\"https://convex.dev/c/ai-town\"\n      target=\"_blank\"\n      className=\"group absolute top-0 left-0 w-64 h-64 md:block z-10 hidden shape-top-left-corner overflow-hidden\"\n      aria-label=\"Powered by Convex\"\n    >\n      <img\n        src={bannerBg}\n        className=\"absolute inset-0 scale-[1.2] -translate-x-6 group-hover:scale-[1.6] transition-transform\"\n        alt=\"\"\n      />\n\n      <div className=\"absolute inset-0 bg-black opacity-0 group-hover:opacity-10 transition-opacity\"></div>\n\n      <div className=\"absolute inset-0 flex p-6\">\n        <div className=\"flex flex-col gap-1 items-center\">\n          <span className=\"font-system font-medium uppercase tracking-wider text-stone-600\">\n            Powered by\n          </span>\n          <svg\n            width=\"126\"\n            height=\"20\"\n            viewBox=\"0 0 126 20\"\n            fill=\"black\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n          >\n            <title>Convex</title>\n            <g clipPath=\"url(#clip0_5_2)\">\n              <path d=\"M3.18483 17.4674C1.30005 15.782 0.357666 13.2908 0.357666 10.0003C0.357666 6.70977 1.31835 4.2186 3.24278 2.53321C5.16415 0.847812 7.79308 0.00350952 11.1265 0.00350952C12.5111 0.00350952 13.7341 0.103028 14.7985 0.308486C15.8629 0.510733 16.8815 0.854231 17.8544 1.34219V6.68088C16.3417 5.92646 14.6246 5.54765 12.7033 5.54765C11.0106 5.54765 9.76021 5.88473 8.95506 6.55889C8.14686 7.23304 7.74429 8.37911 7.74429 10.0003C7.74429 11.5669 8.14076 12.7001 8.93676 13.4C9.72971 14.103 10.9862 14.453 12.7063 14.453C14.527 14.453 16.2563 14.0067 17.8971 13.1175V18.7034C16.0763 19.5669 13.8073 19.9971 11.0899 19.9971C7.70159 19.9971 5.06961 19.1528 3.18483 17.4674Z\" />\n              <path d=\"M19.538 9.99679C19.538 6.73194 20.4224 4.2504 22.1913 2.54896C23.9602 0.847512 26.6257 0 30.1909 0C33.7805 0 36.4644 0.850722 38.2485 2.54896C40.0296 4.24719 40.9201 6.73194 40.9201 9.99679C40.9201 16.6613 37.3427 19.9936 30.1909 19.9936C23.0879 19.9968 19.538 16.6645 19.538 9.99679ZM32.7497 13.3997C33.2743 12.6966 33.5365 11.5634 33.5365 10C33.5365 8.46228 33.2743 7.33547 32.7497 6.61958C32.2251 5.90369 31.3712 5.54735 30.1909 5.54735C29.0381 5.54735 28.2024 5.9069 27.6901 6.61958C27.1777 7.33547 26.9215 8.46228 26.9215 10C26.9215 11.5666 27.1777 12.6998 27.6901 13.3997C28.2024 14.1027 29.035 14.4526 30.1909 14.4526C31.3712 14.4526 32.2221 14.0995 32.7497 13.3997Z\" />\n              <path d=\"M42.6029 0.404494H49.3704L49.5626 1.86196C50.3067 1.32263 51.2552 0.876404 52.408 0.526485C53.5608 0.176565 54.7533 0 55.9854 0C58.2667 0 59.9319 0.5939 60.9841 1.7817C62.0363 2.9695 62.5608 4.80257 62.5608 7.28732V19.5923H55.3328V8.05458C55.3328 7.19101 55.1467 6.57143 54.7747 6.19262C54.4026 5.8138 53.7804 5.62761 52.9082 5.62761C52.3714 5.62761 51.8194 5.75602 51.2552 6.01284C50.691 6.26966 50.2183 6.60032 49.8309 7.00482V19.5923H42.6029V0.404494Z\" />\n              <path d=\"M62.5818 0.404617H70.1178L73.5794 11.6566L77.0409 0.404617H84.5769L77.3855 19.5924H69.7702L62.5818 0.404617Z\" />\n              <path d=\"M86.8523 17.9422C84.6809 16.2279 83.6653 13.252 83.6653 10.0385C83.6653 6.90851 84.4735 4.33066 86.3186 2.54896C88.1637 0.767255 90.9757 0 94.5256 0C97.792 0 100.36 0.796147 102.236 2.38844C104.108 3.98074 105.047 6.15409 105.047 8.9053V12.2665H91.302C91.6436 13.2648 92.0766 13.9872 93.141 14.4334C94.2054 14.8796 95.6907 15.1011 97.5907 15.1011C98.7252 15.1011 99.8841 15.008 101.061 14.8186C101.476 14.7512 102.159 14.6453 102.519 14.565V19.2295C100.723 19.7432 98.3287 20 95.6297 20C91.9973 19.9968 89.0238 19.6565 86.8523 17.9422ZM97.4534 8.13804C97.4534 7.1878 96.4135 5.14286 94.3243 5.14286C92.4396 5.14286 91.1952 7.1557 91.1952 8.13804H97.4534Z\" />\n              <path d=\"M110.723 9.8364L103.955 0.404617H111.799L125.642 19.5924H117.722L114.645 15.3003L111.567 19.5924H103.684L110.723 9.8364Z\" />\n              <path d=\"M117.548 0.404617H125.356L119.363 8.8059L115.398 3.42227L117.548 0.404617Z\" />\n            </g>\n            <defs>\n              <clipPath id=\"clip0_5_2\">\n                <rect width=\"126\" height=\"20\" />\n              </clipPath>\n            </defs>\n          </svg>\n        </div>\n      </div>\n    </a>\n  );\n}\n"
  },
  {
    "path": "src/components/buttons/Button.tsx",
    "content": "import clsx from 'clsx';\nimport { MouseEventHandler, ReactNode } from 'react';\n\nexport default function Button(props: {\n  className?: string;\n  href?: string;\n  imgUrl: string;\n  onClick?: MouseEventHandler;\n  title?: string;\n  children: ReactNode;\n}) {\n  return (\n    <a\n      className={clsx(\n        'button text-white shadow-solid text-xl pointer-events-auto',\n        props.className,\n      )}\n      href={props.href}\n      title={props.title}\n      onClick={props.onClick}\n    >\n      <div className=\"inline-block bg-clay-700\">\n        <span>\n          <div className=\"inline-flex h-full items-center gap-4\">\n            <img className=\"w-4 h-4 sm:w-[30px] sm:h-[30px]\" src={props.imgUrl} />\n            {props.children}\n          </div>\n        </span>\n      </div>\n    </a>\n  );\n}\n"
  },
  {
    "path": "src/components/buttons/InteractButton.tsx",
    "content": "import Button from './Button';\nimport { toast } from 'react-toastify';\nimport interactImg from '../../../assets/interact.svg';\nimport { useConvex, useMutation, useQuery } from 'convex/react';\nimport { api } from '../../../convex/_generated/api';\n// import { SignInButton } from '@clerk/clerk-react';\nimport { ConvexError } from 'convex/values';\nimport { Id } from '../../../convex/_generated/dataModel';\nimport { useCallback } from 'react';\nimport { waitForInput } from '../../hooks/sendInput';\nimport { useServerGame } from '../../hooks/serverGame';\n\nexport default function InteractButton() {\n  // const { isAuthenticated } = useConvexAuth();\n  const worldStatus = useQuery(api.world.defaultWorldStatus);\n  const worldId = worldStatus?.worldId;\n  const game = useServerGame(worldId);\n  const humanTokenIdentifier = useQuery(api.world.userStatus, worldId ? { worldId } : 'skip');\n  const userPlayerId =\n    game && [...game.world.players.values()].find((p) => p.human === humanTokenIdentifier)?.id;\n  const join = useMutation(api.world.joinWorld);\n  const leave = useMutation(api.world.leaveWorld);\n  const isPlaying = !!userPlayerId;\n\n  const convex = useConvex();\n  const joinInput = useCallback(\n    async (worldId: Id<'worlds'>) => {\n      let inputId;\n      try {\n        inputId = await join({ worldId });\n      } catch (e: any) {\n        if (e instanceof ConvexError) {\n          toast.error(e.data);\n          return;\n        }\n        throw e;\n      }\n      try {\n        await waitForInput(convex, inputId);\n      } catch (e: any) {\n        toast.error(e.message);\n      }\n    },\n    [convex],\n  );\n\n  const joinOrLeaveGame = () => {\n    if (\n      !worldId ||\n      // || !isAuthenticated\n      game === undefined\n    ) {\n      return;\n    }\n    if (isPlaying) {\n      console.log(`Leaving game for player ${userPlayerId}`);\n      void leave({ worldId });\n    } else {\n      console.log(`Joining game`);\n      void joinInput(worldId);\n    }\n  };\n  // if (!isAuthenticated || game === undefined) {\n  //   return (\n  //     <SignInButton>\n  //       <Button imgUrl={interactImg}>Interact</Button>\n  //     </SignInButton>\n  //   );\n  // }\n  return (\n    <Button imgUrl={interactImg} onClick={joinOrLeaveGame}>\n      {isPlaying ? 'Leave' : 'Interact'}\n    </Button>\n  );\n}\n"
  },
  {
    "path": "src/components/buttons/LoginButton.tsx",
    "content": "import { SignInButton } from '@clerk/clerk-react';\n\nexport default function LoginButton() {\n  return (\n    <SignInButton>\n      <button className=\"button text-white shadow-solid\">\n        <div className=\"inline-block bg-clay-700\">\n          <span>Log in</span>\n        </div>\n      </button>\n    </SignInButton>\n  );\n}\n"
  },
  {
    "path": "src/components/buttons/MusicButton.tsx",
    "content": "import { useCallback, useEffect, useState } from 'react';\nimport volumeImg from '../../../assets/volume.svg';\nimport { sound } from '@pixi/sound';\nimport Button from './Button';\nimport { useQuery } from 'convex/react';\nimport { api } from '../../../convex/_generated/api';\n\nexport default function MusicButton() {\n  const musicUrl = useQuery(api.music.getBackgroundMusic);\n  const [isPlaying, setPlaying] = useState(false);\n\n  useEffect(() => {\n    if (musicUrl) {\n      sound.add('background', musicUrl).loop = true;\n    }\n  }, [musicUrl]);\n\n  const flipSwitch = async () => {\n    if (isPlaying) {\n      sound.stop('background');\n    } else {\n      await sound.play('background');\n    }\n    setPlaying(!isPlaying);\n  };\n\n  const handleKeyPress = useCallback(\n    (event: { key: string }) => {\n      if (event.key === 'm' || event.key === 'M') {\n        void flipSwitch();\n      }\n    },\n    [flipSwitch],\n  );\n\n  useEffect(() => {\n    window.addEventListener('keydown', handleKeyPress);\n    return () => window.removeEventListener('keydown', handleKeyPress);\n  }, [handleKeyPress]);\n\n  return (\n    <>\n      <Button\n        onClick={() => void flipSwitch()}\n        className=\"hidden lg:block\"\n        title=\"Play AI generated music (press m to play/mute)\"\n        imgUrl={volumeImg}\n      >\n        {isPlaying ? 'Mute' : 'Music'}\n      </Button>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/editor/README.md",
    "content": "# Level Editor\n\n## Setup\n\n1. Run `npm run le` to start the level editor (localhost:5174)\n2. Import a map composite file (.js) into the level config input under the config tab\n3. Customize your tileset by either:\n   - Inputting it in the config tab\n   - Modifying the `DEFAULTTILESETPATH` in leconfig.js (e.g. replace `export const DEFAULTTILESETPATH = \"./tilesets/gentle.png\"` with your tileset path)\n\n## Map Editing Instructions\n\n### Basic Controls\n- Select tiles from the tileset in the top right pane (like selecting a paint brush)\n- Click in the top left pane to place tiles on the background level (no collision)\n- Click in the second row left pane to place tiles on the object level (with collision)\n\n### Keyboard Shortcuts\n- `f` - Fill level 0 with current tile\n- `Ctrl+z` - Undo\n- `g` - Overlay 32x32 grid\n- `s` - Generate .js file to move over to convex/maps/\n- `m` - Place semi-transparent red mask over tiles (helps find invisible tiles)\n- `d` - Hold while clicking a tile to delete\n- `p` - Toggle between 16pixel and 32 pixel\n\n## Map Composite File Structure\n\nAfter saving your map, you will receive a JavaScript export object similar to the following:\n\n```js\nexport const tilesetpath = \"./tilesets/gentle-obj.png\";\nexport const tiledim = 32;\nexport const screenxtiles = 45;\nexport const screenytiles = 32;\nexport const tilesetpxw = 1440;\nexport const tilesetpxh = 1024;\n\nexport const bgtiles = [\n  // 2D array representing the background (no collision)\n];\n\nexport const objmap = [\n  // 2D array representing the object layer (with collision)\n];\n```\n\nExplanation of each property\n- `tilesetpath`: Path to the tileset image that will be used as the “paint brushes” for your map.\n- `tiledim`: The pixel dimension of each tile (e.g., 32px).\n- `screenxtiles / screenytiles`: The map size in terms of tile columns and rows (e.g., 45 tiles wide × 32 tiles tall).\n- `tilesetpxw / tilesetpxh`: The total pixel width and height of your tileset image (not the map).\n- `bgtiles`: A 2D array storing the background layer (no collision).\n- `objmap`: A 2D array storing the object layer (with collision). Each entry is a number referencing the tile coordinate in your tileset.\n\nTo use the new map go to init.js and replace the map composite file path with the new one. and clear the map table in convex with \n\n```bash\njust convex run testing:wipeAllTables\n```\n\nand then run to restart the world\n\n```bash\njust convex run init\n```\n"
  },
  {
    "path": "src/editor/campfire.json",
    "content": "{\"frames\": {\n\n\"pixels_large1.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n},\n\"pixels_large2.png\":\n{\n\t\"frame\": {\"x\":32,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n},\n\"pixels_large3.png\":\n{\n\t\"frame\": {\"x\":64,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n},\n\"pixels_large4.png\":\n{\n\t\"frame\": {\"x\":96,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n}\n},\n\"animations\": {\n\t\"pixels_large\": [\"pixels_large1.png\",\"pixels_large2.png\",\"pixels_large3.png\",\"pixels_large4.png\"]\n},\n\"meta\": {\n\t\"image\": \"./spritesheets/campfire.png\",\n\t\"format\": \"RGBA8888\",\n\t\"size\": {\"w\":128,\"h\":32},\n\t\"scale\": \"1\"\n}\n}\n"
  },
  {
    "path": "src/editor/eutils.js",
    "content": "\n// Function to download data to a file\nexport function download(data, filename, type) {\n    var file = new Blob([data], {type: type});\n    if (window.navigator.msSaveOrOpenBlob) // IE10+\n        window.navigator.msSaveOrOpenBlob(file, filename);\n    else { // Others\n        var a = document.createElement(\"a\"),\n                url = URL.createObjectURL(file);\n        a.href = url;\n        a.download = filename;\n        document.body.appendChild(a);\n        a.click();\n        setTimeout(function() {\n            document.body.removeChild(a);\n            window.URL.revokeObjectURL(url);  \n        }, 0); \n    }\n}"
  },
  {
    "path": "src/editor/gentlesparkle.json",
    "content": "{\"frames\": {\n\n\"pixels_large1.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n},\n\"pixels_large2.png\":\n{\n\t\"frame\": {\"x\":32,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n},\n\"pixels_large3.png\":\n{\n\t\"frame\": {\"x\":64,\"y\":0,\"w\":32,\"h\":32},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":32},\n\t\"sourceSize\": {\"w\":32,\"h\":32}\n}\n},\n\"animations\": {\n\t\"pixels_large\": [\"pixels_large1.png\",\"pixels_large2.png\",\"pixels_large3.png\"]\n},\n\"meta\": {\n\t\"image\": \"./spritesheets/gentlesparkle32.png\",\n\t\"format\": \"RGBA8888\",\n\t\"size\": {\"w\":192,\"h\":320},\n\t\"scale\": \"1\"\n}\n}"
  },
  {
    "path": "src/editor/gentlesplash.json",
    "content": "{\"frames\": {\n\n\"pixels_large1.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n},\n\"pixels_large2.png\":\n{\n\t\"frame\": {\"x\":32,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n},\n\"pixels_large3.png\":\n{\n\t\"frame\": {\"x\":64,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n},\n\"pixels_large4.png\":\n{\n\t\"frame\": {\"x\":64,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n},\n\"pixels_large5.png\":\n{\n\t\"frame\": {\"x\":128,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n},\n\"pixels_large6.png\":\n{\n\t\"frame\": {\"x\":160,\"y\":192,\"w\":32,\"h\":64},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n\t\"sourceSize\": {\"w\":32,\"h\":64}\n}\n},\n\"animations\": {\n\t\"pixels_large\": [\"pixels_large1.png\",\"pixels_large2.png\",\"pixels_large3.png\",\"pixels_large4.png\",\"pixels_large5.png\",\"pixels_large6.png\"]\n},\n\"meta\": {\n\t\"image\": \"./spritesheets/gentlewaterfall32.png\",\n\t\"format\": \"RGBA8888\",\n\t\"size\": {\"w\":192,\"h\":320},\n\t\"scale\": \"1\"\n}\n}"
  },
  {
    "path": "src/editor/gentlewaterfall.json",
    "content": "{\"frames\": {\n\n\"pixels_large1.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n},\n\"pixels_large2.png\":\n{\n\t\"frame\": {\"x\":32,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n},\n\"pixels_large3.png\":\n{\n\t\"frame\": {\"x\":64,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n},\n\"pixels_large4.png\":\n{\n\t\"frame\": {\"x\":96,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n},\n\"pixels_large5.png\":\n{\n\t\"frame\": {\"x\":128,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n},\n\"pixels_large6.png\":\n{\n\t\"frame\": {\"x\":160,\"y\":32,\"w\":32,\"h\":96},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":96},\n\t\"sourceSize\": {\"w\":32,\"h\":96}\n}\n},\n\"animations\": {\n\t\"pixels_large\": [\"pixels_large1.png\",\"pixels_large2.png\",\"pixels_large3.png\",\"pixels_large4.png\",\"pixels_large5.png\",\"pixels_large6.png\"]\n},\n\"meta\": {\n\t\"image\": \"./spritesheets/gentlewaterfall32.png\",\n\t\"format\": \"RGBA8888\",\n\t\"size\": {\"w\":192,\"h\":320},\n\t\"scale\": \"1\"\n}\n}"
  },
  {
    "path": "src/editor/index.html",
    "content": "<html>\n<head>\n<body>\n<a href=\"./le.html\">Level editer</a><br>\n<a href=\"./se.html\">Sprite editer</a>\n</body>\n</html>\n"
  },
  {
    "path": "src/editor/le.html",
    "content": "<html>\n<head>\n<style>\n    table, th, td {\n        border: 1px solid black;\n        border-radius: 10px;\n    }\n </style>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n</head>\n<body>\n    <script src=\"le.js\" type=\"module\"></script>\n\n    <div class=\"tab\">\n        <button class=\"tablinks\" onclick=\"onTab(event, 'edit')\">edit</button>\n        <button class=\"tablinks\" onclick=\"onTab(event, 'map')\">map</button>\n        <button class=\"tablinks\" onclick=\"onTab(event, 'config')\">config</button>\n      </div>\n\n<div id=\"edit\" class=\"tabcontent\">\n    <TABLE WIDTH=\"100%\"><TR>\n        <TD ALIGN=\"center\"> <!-- first layer of map tiles -->\n          <div ID=\"layer0pane\" style=\"max-height: 480px;max-width:640px;overflow: scroll;\">\n          <canvas ID=\"level0\" height=\"1600px\" width=\"1600px\" ></canvas> </div>   \n        </TD>\n        <TD ALIGN=\"center\"> <!-- tile set-->\n           <div ID=\"tilesetpane\" style=\"max-height: 600;max-width:800;overflow: scroll;\">\n           <CANVAS ID=\"tileset\" width=\"5632px\" height=\"8672px\" STYLE=\"border: 1px dotted white;\"></CANVAS> </div>   \n        </TD>\n        </TR>\n\n        <tr>\n        <TD ALIGN=\"center\"> <!-- second layer of map tiles -->\n        <div ID=\"layer1pane\" style=\"max-height: 480px;max-width:640px;overflow: scroll;\">\n            <canvas ID=\"level1\" height=\"1600px\" width=\"1600px\" ></canvas>\n        </div></TD>\n        <TD ALIGN=\"center\"> <!-- aggregate map view -->\n        <div ID=\"compositepane\" style=\"max-height: 600;max-width:800;overflow: scroll;\">\n            <canvas ID=\"composite\" height=\"1600px\" width=\"1600px\" ></canvas>\n        </div></TD>\n        </tr>\n\n        <tr>\n        <TD ALIGN=\"center\"> <!-- object layer of map -->\n        <div ID=\"layer2pane\" style=\"max-height: 480px;max-width:640px;overflow: scroll;\">\n            <canvas ID=\"level3\" height=\"1600px\" width=\"1600px\" ></canvas>\n        </div></TD>\n        <TD ALIGN=\"center\"> <!-- object layer of map -->\n        <div ID=\"layer3pane\" style=\"max-height: 480px;max-width:640px;overflow: scroll;\">\n            <canvas ID=\"level4\" height=\"1600px\" width=\"1600px\" ></canvas>\n        </div></TD>\n        </tr>\n    </TABLE>\n</div>\n\n<div id=\"map\" class=\"tabcontent\">\n        <canvas id=\"mapcanvas\" height=\"1600px\" width=\"1600px\" ></canvas>\n</div>\n\n<div id=\"config\" class=\"tabcontent\">\n            <table>\n            <tr>\n                <td>Composite to png</td>\n                <td>Load level</td>\n                <td>Load png to Composite</td>\n                <td>Load Sprite</td>\n                <td></td>\n            </tr>\n            <tr>\n            <td>\n            <button onclick = \"saveCompositeAsImage()\" > Save Composite </button>\n            </td><td>\n            <input type=\"file\" multiple id=\"levelfile\">\n            </td><td>\n            <input type=\"file\" multiple id=\"compositepng\">\n            </td><td>\n            <input type=\"file\" multiple id=\"spritesheet\">\n            </td><td>\n            <form name=\"myForm\">\n                16 <input type=\"radio\" name=\"radioTiledim\"  value=\"16\" />\n                32 <input type=\"radio\" name=\"radioTiledim\"  value=\"32\" checked />\n              </form>\n              </td>\n            </tr>\n\n            <tr>\n                <td>Load Tileset</td>\n            </tr>\n            <tr>\n                <td> <input type=\"file\" multiple id=\"tilesetfile\"> </td>\n            </tr>\n\n\n            </table>\n</div>\n\n\n</body>\n</html>"
  },
  {
    "path": "src/editor/le.js",
    "content": "// --\n// Simple level editer. \n//\n// TODO:\n//  -- right now if plaxing a sprite, will place based on selected tiles. So need to clear that when\n//     loading a sprite\n//  -- fix hardcoded animations, hack of putting spritesheet into g_ctx etc\n//  -- create tab that contains all animations for a given json file \n//  -- add portals to level for character start positions\n//  -- if you load an animated sprite and then load a level, it just puts the sprite everywhere\n// \n// \n// Done:\n//  -- fix level load bug where texture doesn't fit (load, mage, serene and then gentle)\n//  -- write maps with sprites\n//  - <esc> clear selected_tiles\n//  - Delete tiles\n//  - move magic numbers to context / initialization (zIndex, pane size etc.)\n//  - todo fudge factor on g_ctx.tileset \n//  - get rid of dangerous CONFIG.tiledim (use g_ctx.tileDim instead)\n//  - XXX create tilesetpadding for tilesets whos tiles are spaced (e.g. phantasy star II)\n//  - only use fudge to pick sprites rather than fudge and non\n//  - use g_ctx for g_ctx.tileset parameters instead of CONFIG (starting with initTilesetConfig) \n//  - todo print locations on screen\n//\n// \n// Keybindings:\n// f - fill level 0 with current tile\n// <ctl>-z - undo\n// g - overlay 32x32 grid\n// s - generate .js file to move over to convex/maps/\n// m - place a semi-transparent red mask over all tiles. This helps find invisible tiles\n// d - hold while clicking a tile to delete\n// p - toggle between 16pixel and 32 pixel. \n// \n// Known bugs and annoyances\n//  - if deleting a tile while filter is on, filter isn't refreshed so need to toggle with \"m\"\n// --\n\nimport * as PIXI from 'pixi.js'\nimport { g_ctx }  from './lecontext.js' // global context\nimport * as CONFIG from './leconfig.js' \nimport * as UNDO from './undo.js'\nimport * as MAPFILE from './mapfile.js'\nimport * as UI from './lehtmlui.js'\nimport { EventSystem } from '@pixi/events';\n\ng_ctx.debug_flag  = true;\ng_ctx.debug_flag2 = false; // really verbose output\n\nfunction tileset_index_from_coords(x, y) {\n    let retme = x + (y*g_ctx.tilesettilew);\n    console.log(\"tileset_index_from_coord \",retme, x, y);\n    return retme; \n}\nfunction level_index_from_coords(x, y) {\n    // place 16px tiles in separate index space\n    let offset = (g_ctx.tiledimx == 16)? CONFIG.MAXTILEINDEX : 0;\n    let retme = x + (y*CONFIG.leveltilewidth) + offset; \n    return retme;\n}\nfunction tileset_index_from_px(x, y) {\n    let coord_x = Math.floor(x / (g_ctx.tiledimx + CONFIG.tilesetpadding));\n    let coord_y = Math.floor(y / (g_ctx.tiledimx+ CONFIG.tilesetpadding));\n\n    console.log(\"tileset_index_from_px \",x, y);\n\n    return tileset_index_from_coords(coord_x, coord_y); \n}\nfunction level_index_from_px(x, y) {\n    let coord_x = Math.floor(x / g_ctx.tiledimx);\n    let coord_y = Math.floor(y / g_ctx.tiledimx);\n    return level_index_from_coords(coord_x, coord_y); \n}\n\nfunction tileset_coords_from_index(index) {\n        let x = index % (g_ctx.tilesettilew);\n        let y = Math.floor(index / (g_ctx.tilesettilew));\n        // console.log(\"tilesettilewidth: \",g_ctx.tilesettilew);\n        // console.log(\"tileset_coords_from_index tile coords: \",index,x,y);\n        return [x,y];\n}\n\nfunction tileset_px_from_index(index) {\n        let ret = tileset_coords_from_index(index); \n        return [ret[0] * (g_ctx.tiledimx+CONFIG.tilesetpadding), ret[1] * (g_ctx.tiledimx+CONFIG.tilesetpadding)] ;\n}\n\n\n// return a sprite of size tileDim given (x,y) starting location\nfunction sprite_from_px(x, y) {\n\n    const bt = PIXI.BaseTexture.from(g_ctx.tilesetpath, {\n        scaleMode: PIXI.SCALE_MODES.NEAREST,\n    });\n    let texture = new PIXI.Texture(bt,\n                new PIXI.Rectangle(x, y, g_ctx.tiledimx, g_ctx.tiledimx),\n            );\n    return new PIXI.Sprite(texture);\n}\n\nfunction DragState() {\n    this.square  = new PIXI.Graphics();\n    this.tooltip = new PIXI.Text('', {\n        fontFamily: 'Courier',\n        fontSize: 12,\n        fill: 0xffffff,\n        align: 'center',\n    });\n    this.startx  = 0;\n    this.starty = 0;\n    this.endx   = 0;\n    this.endy   = 0;\n}\n\nclass LayerContext {\n\n    constructor(app, pane, num, mod = null) {\n        this.app = app;\n        this.scrollpane = pane;\n        this.num = num;\n        this.widthpx  = CONFIG.levelwidth;\n        this.heightpx = CONFIG.levelheight;\n\n\n        this.container = new PIXI.Container();\n        this.sprites = {};\n        this.composite_sprites = {};\n        this.dragctx = new DragState();\n\n        app.stage.addChild(this.container);\n\n        this.mouseshadow    = new PIXI.Container(); \n        this.mouseshadow.zIndex = CONFIG.zIndexMouseShadow; \n\n        this.lasttileindex  = -1;  // current tileset index\n        this.curanimatedtile = null;\n\n        this.fudgex = 0; // offset from 0,0\n        this.fudgey = 0;\n\n        this.square = new PIXI.Graphics();\n        this.square.beginFill(0x2980b9);\n        this.square.drawRect(0, 0, CONFIG.levelwidth, CONFIG.levelheight);\n        this.square.endFill();\n        this.square.eventMode = 'static';\n        this.container.addChild(this.square);\n\n        this.square.on('mousemove', onLevelMousemove.bind(this));\n        this.square.on('mouseover', onLevelMouseover.bind(this));\n        this.square.on('pointerout', onLevelMouseOut.bind(this))\n        this.square.on('pointerdown', onLevelPointerDown.bind(null, this))\n            .on('pointerup', onLevelDragEnd.bind(null, this))\n            .on('pointerupoutside', onLevelDragEnd.bind(null, this));\n\n        if (mod != null && !(mod  === g_ctx)) {\n            this.loadFromMapFile(mod);\n        }\n    }\n\n    loadFromMapFile(mod) {\n        let tiles = [];\n        if (this.num == 0) {\n            tiles = mod.bgtiles[0];\n        } else if (this.num == 1) {\n            tiles = mod.bgtiles[1];\n        } else if (this.num == 2) {\n            tiles = mod.objmap[0];\n        } else if (this.num == 3) {\n            tiles = mod.objmap[1];\n        } else {\n            console.log(\"loadFromMapFile: Error unknow layer number\");\n            return;\n        }\n\n        for (let x = 0; x < tiles.length; x++) {\n            for (let y = 0; y < tiles[0].length; y++) {\n                if (tiles[x][y] != -1) {\n                    this.addTileLevelCoords(x, y, mod.tiledim, tiles[x][y]);\n                }\n            }\n        }\n    }\n\n    //  this will create a rectangle with an alpha channel for every square that has a sprite. This helps find \n    //  sprites that are purely transparent\n    drawFilter() {\n\n        if (typeof this.filtergraphics == 'undefined') {\n            this.filtertoggle = true;\n            this.filtergraphics = new PIXI.Graphics();\n            this.filtergraphics.zIndex = CONFIG.zIndexFilter;\n        }\n\n        if (this.filtertoggle) {\n\n            this.filtergraphics.beginFill(0xff0000, 0.3);\n            for (let i in this.sprites) {\n                let spr = this.sprites[i];\n                this.filtergraphics.drawRect(spr.x, spr.y, g_ctx.tiledimx, g_ctx.tiledimx);\n            }\n            this.filtergraphics.endFill();\n            this.container.addChild(this.filtergraphics);\n        }else{\n            this.filtergraphics.clear();\n            this.container.removeChild(this.filtergraphics);\n        }\n\n        this.filtertoggle = ! this.filtertoggle;\n    }\n\n    // add tile of \"index\" to Level at location x,y\n    addTileLevelCoords(x, y, dim, index) {\n        return this.addTileLevelPx(x * dim, y * dim, index);\n    }\n\n    // add tile of tileset \"index\" to Level at location x,y\n    addTileLevelPx(x, y, index) {\n\n        if (x > CONFIG.levelwidth || y > CONFIG.levelheight){\n            console.log(\"tile placed outside of level boundary, ignoring\",x,y)\n            return -1;\n        } \n\n        let xPx = x;\n        let yPx = y;\n\n        let ctile = null;\n        let ctile2 = null;\n\n        if(g_ctx.spritesheet != null){\n            ctile  =  new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']);\n            ctile2 =  new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']);\n            ctile.animationSpeed = .1;\n            ctile2.animationSpeed = .1;\n            ctile.autoUpdate = true;\n            ctile2.autoUpdate = true;\n            ctile.play();\n            ctile2.play();\n\n            // HACK for now just stuff animated sprite details into the sprite\n            ctile.animationname   = 'row0';\n            ctile.spritesheetname = g_ctx.spritesheetname; \n\n        } else {\n            let pxloc = tileset_px_from_index(index);\n            ctile = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n            ctile.index = index;\n            ctile2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n        }\n\n        // snap to grid\n        const dx = g_ctx.tiledimx;\n        const dy = g_ctx.tiledimy;\n        ctile.x  = Math.floor(xPx / dx) * dx; \n        ctile2.x = Math.floor(xPx / dx) * dx; \n        ctile.y  = Math.floor(yPx / dy) * dy;\n        ctile2.y = Math.floor(yPx / dy) * dy;\n        ctile2.zIndex = this.num; \n\n        // console.log(xPx,yPx,ctile.x,ctile.y);\n\n        let new_index = level_index_from_px(ctile.x, ctile.y);\n\n        if(g_ctx.debug_flag2){\n            console.log('addTileLevelPx ',this.num,' ctile.x ', ctile.x, 'ctile.y ', ctile.y, \"index \", index, \"new_index\", new_index);\n        }\n\n        if (!g_ctx.dkey) {\n            this.container.addChild(ctile);\n            g_ctx.composite.container.addChild(ctile2);\n        } \n\n\n        if (this.sprites.hasOwnProperty(new_index)) {\n            if(g_ctx.debug_flag){\n             console.log(\"addTileLevelPx: \",this.num,\"removing old tile\", new_index);\n            }\n            this.container.removeChild(this.sprites[new_index]);\n            delete this.sprites[new_index];\n            g_ctx.composite.container.removeChild(this.composite_sprites[new_index]);\n            delete this.composite_sprites[new_index];\n        }\n\n        if (!g_ctx.dkey) {\n            this.sprites[new_index] = ctile;\n            this.composite_sprites[new_index] = ctile2;\n        } else if (typeof this.filtergraphics != 'undefined') {\n            this.filtergraphics.clear();\n            this.drawFilter();\n            this.drawFilter();\n        }\n\n        // consolelog(\"SETTING ZINDEX \", this.composite_sprites[new_index].zIndex);\n        return new_index;\n    }\n\n} // class  LayerContext\n\nclass TilesetContext {\n\n    constructor(app, mod = g_ctx) {\n        this.app = app;\n        this.container = new PIXI.Container();\n\n        this.widthpx  = g_ctx.tilesetpxw;\n        this.heightpx = g_ctx.tilesetpxh;\n        console.log(mod.tilesetpath);\n        const texture = PIXI.Texture.from(mod.tilesetpath);\n        const bg    = new PIXI.Sprite(texture);\n\n        this.square = new PIXI.Graphics();\n        this.square.beginFill(0x2980b9);\n        this.square.drawRect(0, 0, mod.tilesetpxw, mod.tilesetpxh);\n        this.square.endFill();\n        this.square.eventMode = 'static';\n        this.container.addChild(this.square);\n        this.container.addChild(bg);\n        \n        this.app.stage.addChild(this.container);\n\n        this.fudgex = 0; // offset from 0,0\n        this.fudgey = 0;\n\n        this.dragctx = new DragState();\n\n        this.square.on('mousedown', function (e) {\n\n            // if a spritesheet has been loaded from a file, delete\n            // FIXME, we should be able to add animated tiles to the \n            // tileset ... \n            if(g_ctx.spritesheet != null){\n                // FIXME .. creating a leak here. But animatedsprites are still on the map so\n                // cannot destroy. In the future these should be part of the UI \n                // g_ctx.spritesheet.destroy();\n                g_ctx.spritesheet = null;\n            }\n\n            g_ctx.tile_index = tileset_index_from_px(e.global.x, e.global.y); \n\n            if(g_ctx.debug_flag) {\n                console.log(\"g_ctx.tileset mouse down. index \"+g_ctx.tile_index);\n            }\n        });\n\n        this.square.on('pointerdown', onTilesetDragStart)\n                .on('pointerup', onTilesetDragEnd)\n                .on('pointerupoutside', onTilesetDragEnd);\n    }\n\n    addTileSheet(name, sheet){\n        console.log(\" tileset.addTileSheet \", sheet);\n\n\n        // FIXME ... development code\n        g_ctx.spritesheet = sheet;\n        g_ctx.spritesheetname = name;\n\n        let as =  new PIXI.AnimatedSprite(sheet.animations['row0']);\n        as.animationSpeed = .1;\n        as.autoUpdate = true;\n        as.play();\n        as.alpha = .5;\n        g_ctx.g_layers[0].curanimatedtile = as;\n    }\n} // class TilesetContext\n\n\nclass CompositeContext {\n\n    constructor(app) {\n        this.app = app;\n        this.widthpx  = CONFIG.levelwidth;\n        this.heightpx = CONFIG.levelheight;\n\n        this.container = new PIXI.Container();\n        this.container.sortableChildren = true;\n        this.app.stage.addChild(this.container);\n        this.sprites = {};\n        this.circle = new PIXI.Graphics();\n        this.circle.zIndex = CONFIG.zIndexCompositePointer;\n\n        this.fudgex = 0; // offset from 0,0\n        this.fudgey = 0;\n\n        this.mouseshadow    = new PIXI.Container(); \n        this.mouseshadow.zIndex = CONFIG.zIndexMouseShadow; \n        this.lasttileindex  = -1; \n\n        this.square = new PIXI.Graphics();\n        this.square.beginFill(0x2980b9);\n        this.square.drawRect(0, 0, CONFIG.levelwidth, CONFIG.levelheight);\n        this.square.endFill();\n        this.square.eventMode = 'static';\n        this.container.addChild(this.square);\n\n        this.square.on('mousedown', onCompositeMousedown.bind(null, this));\n    }\n\n} // class CompositeContext\n\nfunction loadAnimatedSpritesFromModule(mod){\n\n    if(!('animatedsprites' in mod) || mod.animatedsprites.length <= 0){\n        return;\n    }\n\n    let m = new Map();\n\n    for(let x = 0; x < mod.animatedsprites.length; x++){\n        let spr = mod.animatedsprites[x];\n        if(! m.has(spr.sheet)){\n            m.set(spr.sheet, [spr]);\n        }else{\n            m.get(spr.sheet).push(spr);\n        }\n    }\n\n    for(let key of m.keys()){\n        console.log(\"loadAnimatedSpritesFromModule: \",key);\n        PIXI.Assets.load(\"./\"+key).then(\n            function(sheet) {\n\n                // setup global state so we can use layer addTileLevelMethod\n                g_ctx.spritesheet     = sheet;\n                g_ctx.spritesheetname = key;\n                let asprarray = m.get(key);\n                for (let asprite of asprarray) {\n                    // TODO FIXME, pass in animation name\n                    console.log(\"Loading animation\", asprite.animation);\n                    g_ctx.g_layers[asprite.layer].addTileLevelPx(asprite.x, asprite.y, -1);\n                }\n                g_ctx.spritesheet     = null;\n                g_ctx.spritesheetname = null;\n            }\n        );\n    }\n}\n\nfunction loadMapFromModuleFinish(mod) {\n    g_ctx.composite.container.removeChildren();\n    g_ctx.tileset_app.stage.removeChildren()\n    g_ctx.tileset = new TilesetContext(g_ctx.tileset_app, mod);\n    g_ctx.g_layer_apps[0].stage.removeChildren()\n    g_ctx.g_layers[0] = new LayerContext(g_ctx.g_layer_apps[0], document.getElementById(\"layer0pane\"), 0, mod);\n    g_ctx.g_layer_apps[1].stage.removeChildren()\n    g_ctx.g_layers[1] = new LayerContext(g_ctx.g_layer_apps[1], document.getElementById(\"layer1pane\"), 1, mod);\n    g_ctx.g_layer_apps[2].stage.removeChildren()\n    g_ctx.g_layers[2] = new LayerContext(g_ctx.g_layer_apps[2], document.getElementById(\"layer2pane\"), 2, mod);\n    g_ctx.g_layer_apps[3].stage.removeChildren()\n    g_ctx.g_layers[3] = new LayerContext(g_ctx.g_layer_apps[3], document.getElementById(\"layer3pane\"), 3, mod);\n\n    loadAnimatedSpritesFromModule(mod);\n}\n\nfunction loadMapFromModule(mod) {\n    g_ctx.tilesetpath = mod.tilesetpath;\n    initTilesSync(loadMapFromModuleFinish.bind(null, mod));\n    initTiles();\n}\n\nfunction downloadpng(filename) {\n    let newcontainer = new PIXI.Container();\n    let children = [...g_ctx.composite.container.children];\n    for(let i = 0; i <  children.length; i++) {\n        let child = children[i];\n        if (! child.hasOwnProperty('isSprite') || !child.isSprite){\n            console.log(child);\n            continue;\n        }\n        // console.log(child, typeof child);\n        g_ctx.composite.container.removeChild(child);\n        newcontainer.addChild(child);\n    }\n\n      const { renderer } = g_ctx.composite_app;\n      renderer.plugins.extract.canvas(newcontainer).toBlob(function (b) {\n\n      console.log(b);\n      var a = document.createElement(\"a\");\n      document.body.append(a);\n      a.download = filename;\n      a.href = URL.createObjectURL(b);\n      a.click();\n      a.remove();\n    }, \"image/png\");\n  }\n\nwindow.saveCompositeAsImage = () => {\n    downloadpng(\"g_ctx.composite.png\");\n}\n\nwindow.onTab = (evt, tabName) => {\n    // Declare all variables\n    var i, tabcontent, tablinks;\n\n    // Get all elements with class=\"tabcontent\" and hide them\n    tabcontent = document.getElementsByClassName(\"tabcontent\");\n    for (i = 0; i < tabcontent.length; i++) {\n        tabcontent[i].style.display = \"none\";\n    }\n\n    // Get all elements with class=\"tablinks\" and remove the class \"active\"\n    tablinks = document.getElementsByClassName(\"tablinks\");\n    for (i = 0; i < tablinks.length; i++) {\n        tablinks[i].className = tablinks[i].className.replace(\" active\", \"\");\n    }\n\n    // Show the current tab, and add an \"active\" class to the button that opened the tab\n    document.getElementById(tabName).style.display = \"block\";\n    evt.currentTarget.className += \" active\";\n\n    if (tabName == \"map\"){\n        g_ctx.map_app.stage.addChild(g_ctx.composite.container);\n    }else {\n        g_ctx.composite.app.stage.addChild(g_ctx.composite.container);\n    }\n}\n\n// fill base level with currentIndex tile \nwindow.fill0 = () => {\n    UNDO.undo_mark_task_start(g_ctx.g_layers[0]);\n    for(let i = 0; i < CONFIG.levelwidth / g_ctx.tiledimx; i++){\n        for(let j = 0; j < CONFIG.levelheight / g_ctx.tiledimx; j++){\n            let ti = g_ctx.g_layers[0].addTileLevelCoords(i,j,g_ctx.tiledimx, g_ctx.tile_index);\n            UNDO.undo_add_index_to_task(ti);\n        }\n    }\n    UNDO.undo_mark_task_end();\n}\n\nwindow.addEventListener(\n    \"keyup\", (event) => {\n        if (event.code == \"KeyD\"){\n            g_ctx.dkey = false;\n            g_ctx.g_layers.map( (l) => l.container.addChild(l.mouseshadow));\n            g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow);\n        }\n    });\nwindow.addEventListener(\n    \"keydown\", (event) => {\n\n        if (event.code == \"KeyD\"){\n            g_ctx.dkey = true;\n            g_ctx.g_layers.map((l) => l.container.removeChild(l.mouseshadow) );\n            g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow);\n        }\n\n        if (event.code == 'KeyF'){\n            window.fill0();\n        }\n        else if (event.code == 'KeyS'){\n            MAPFILE.generate_level_file();\n        }\n        else if (event.code == 'Escape'){\n            g_ctx.selected_tiles = [];\n            g_ctx.g_layers.map((l) => l.mouseshadow.removeChildren());\n            g_ctx.composite.mouseshadow.removeChildren();\n        }\n        else if (event.code == 'KeyM'){\n            g_ctx.g_layers.map((l) => l.drawFilter () );\n        }else if (event.code == 'KeyP'){\n            setGridSize((g_ctx.tiledimx == 16)?32:16);\n        }\n        else if (event.code == 'KeyG'){\n            g_ctx.g_layers.map((l) => redrawGrid (l, false) );\n            redrawGrid(g_ctx.tileset, false); \n            redrawGrid(g_ctx.composite, false); \n        }\n        else if (event.ctrlKey && event.code === 'KeyZ'){\n            let undome = UNDO.undo_pop();\n            if (!undome) {\n                return;\n            }\n            let layer = undome.shift();\n            for(let i = 0; i < undome.length; i++) {\n                if (g_ctx.debug_flag) {\n                    console.log(\"Undo removing \", undome[i])\n                }\n                // Remove current tile\n                layer.container.removeChild(layer.sprites[undome[i][0]]);\n                g_ctx.composite.container.removeChild(layer.composite_sprites[undome[i][0]]);\n                \n                // Restore original tile if it existed\n                if (undome[i][1] !== -1) {\n                    let pxloc = tileset_px_from_index(undome[i][1]);\n                    let originalTile = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n                    let originalTile2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n                    \n                    // Position tiles at the correct location\n                    let x = Math.floor(undome[i][0] % CONFIG.leveltilewidth) * g_ctx.tiledimx;\n                    let y = Math.floor(undome[i][0] / CONFIG.leveltilewidth) * g_ctx.tiledimx;\n                    originalTile.x = x;\n                    originalTile.y = y;\n                    originalTile2.x = x;\n                    originalTile2.y = y;\n                    originalTile2.zIndex = layer.num;\n                    \n                    // Add tiles back to containers\n                    layer.container.addChild(originalTile);\n                    g_ctx.composite.container.addChild(originalTile2);\n                    \n                    // Update sprite references\n                    layer.sprites[undome[i][0]] = originalTile;\n                    layer.composite_sprites[undome[i][0]] = originalTile2;\n                } else {\n                    // If there was no original tile, delete the sprite references\n                    delete layer.sprites[undome[i][0]];\n                    delete layer.composite_sprites[undome[i][0]];\n                }\n            }\n        }\n        else if (event.shiftKey && event.code == 'ArrowUp') {\n            g_ctx.tileset.fudgey -= 1;\n            redrawGrid(g_ctx.tileset, true);\n        }\n        else if (event.shiftKey && event.code == 'ArrowDown') {\n            g_ctx.tileset.fudgey += 1;\n            redrawGrid(g_ctx.tileset, true);\n        }\n        else if (event.shiftKey && event.code == 'ArrowLeft') {\n            g_ctx.tileset.fudgex -= 1;\n            redrawGrid(g_ctx.tileset, true);\n        }\n        else if (event.shiftKey && event.code == 'ArrowRight') {\n            g_ctx.tileset.fudgex += 1;\n            redrawGrid(g_ctx.tileset, true);\n        }\n     }\n  );\n\n// Listen to pointermove on stage once handle is pressed.\n\nfunction onTilesetDragStart(e)\n{\n    if (g_ctx.debug_flag) {\n        console.log(\"onDragStartTileset()\");\n    }\n    g_ctx.tileset.app.stage.eventMode = 'static';\n    g_ctx.tileset.app.stage.addEventListener('pointermove', onTilesetDrag);\n    \n    g_ctx.tileset.dragctx.startx = e.data.global.x;\n    g_ctx.tileset.dragctx.starty = e.data.global.y;\n    g_ctx.tileset.dragctx.endx = e.data.global.x;\n    g_ctx.tileset.dragctx.endy = e.data.global.y;\n\n    g_ctx.tileset.app.stage.addChild(g_ctx.tileset.dragctx.square);\n    // g_ctx.tileset.app.stage.addChild(g_ctx.tileset.dragctx.tooltip);\n\n    g_ctx.selected_tiles = [];\n}\n\n// Stop dragging feedback once the handle is released.\nfunction onTilesetDragEnd(e)\n{\n    if (g_ctx.debug_flag) {\n        console.log(\"onDragEndTileset()\");\n    }\n\n    g_ctx.tileset.app.stage.eventMode = 'auto';\n    g_ctx.tileset.app.stage.removeEventListener('pointermove', onTilesetDrag);\n    g_ctx.tileset.app.stage.removeChild(g_ctx.tileset.dragctx.square);\n    g_ctx.tileset.app.stage.removeChild(g_ctx.tileset.dragctx.tooltip);\n\n\n    if(g_ctx.tileset.dragctx.endx < g_ctx.tileset.dragctx.startx){\n        let tmp = g_ctx.tileset.dragctx.endx;\n        g_ctx.tileset.dragctx.endx = g_ctx.tileset.dragctx.startx;\n        g_ctx.tileset.dragctx.startx = tmp;\n    }\n    if(g_ctx.tileset.dragctx.endy < g_ctx.tileset.dragctx.starty){\n        let tmp = g_ctx.tileset.dragctx.endy;\n        g_ctx.tileset.dragctx.endy = g_ctx.tileset.dragctx.starty;\n        g_ctx.tileset.dragctx.starty = tmp;\n    }\n\n    let starttilex = Math.floor(g_ctx.tileset.dragctx.startx / g_ctx.tiledimx);\n    let starttiley = Math.floor(g_ctx.tileset.dragctx.starty / g_ctx.tiledimx);\n    let endtilex = Math.floor(g_ctx.tileset.dragctx.endx / g_ctx.tiledimx);\n    let endtiley = Math.floor(g_ctx.tileset.dragctx.endy / g_ctx.tiledimx);\n\n    if (g_ctx.debug_flag) {\n        console.log(\"sx sy ex ey \", starttilex, \",\", starttiley, \",\", endtilex, \",\", endtiley);\n    }\n    // let mouse clicked handle if there isn't a multiple tile square\n    if(starttilex === endtilex && starttiley === endtiley ){\n        return;\n    }\n\n//    g_ctx.tile_index = (starttiley * g_ctx.tilesettilew) + starttilex;\n\n    g_ctx.tile_index = tileset_index_from_px(e.global.x, e.global.y); \n\n    let origx = starttilex;\n    let origy = starttiley;\n    for(let y = starttiley; y <= endtiley; y++){\n        for(let x = starttilex; x <= endtilex; x++){\n            let squareindex = (y * g_ctx.tilesettilew) + x;\n            g_ctx.selected_tiles.push([x - origx,y - origy,squareindex]);\n        }\n    }\n    g_ctx.tileset.dragctx.square.clear();\n    // g_ctx.tileset.dragctx.tooltip.clear();\n}\n\nfunction onTilesetDrag(e)\n{\n    if (g_ctx.debug_flag) {\n        console.log(\"onDragTileset()\");\n    }\n    g_ctx.tileset.dragctx.endx = e.global.x;\n    g_ctx.tileset.dragctx.endy = e.global.y;\n    \n    g_ctx.tileset.dragctx.square.clear();\n    g_ctx.tileset.dragctx.square.beginFill(0xFF3300, 0.3);\n    g_ctx.tileset.dragctx.square.lineStyle(2, 0xffd900, 1);\n    g_ctx.tileset.dragctx.square.moveTo(g_ctx.tileset.dragctx.startx, g_ctx.tileset.dragctx.starty);\n    g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.endx, g_ctx.tileset.dragctx.starty);\n    g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.endx, g_ctx.tileset.dragctx.endy);\n    g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.startx, g_ctx.tileset.dragctx.endy);\n    g_ctx.tileset.dragctx.square.closePath();\n    g_ctx.tileset.dragctx.square.endFill();\n\n\n    // g_ctx.tileset.dragctx.tooltip.clear();\n    // g_ctx.tileset.dragctx.tooltip.beginFill(0xFF3300, 0.3);\n    // g_ctx.tileset.dragctx.tooltip.lineStyle(2, 0xffd900, 1);\n    // g_ctx.tileset.dragctx.tooltip.drawRect(e.global.x, e.global.y, 20,8);\n    // g_ctx.tileset.dragctx.tooltip.endFill();\n}\n\n//g_ctx.tileset.app.stage.addChild(g_ctx.tileset.container);\n\nfunction redrawGrid(pane, redraw = false) {\n\n    if (typeof pane.gridtoggle == 'undefined') {\n        // first time we're being called, initialized\n        pane.gridtoggle  = false;\n        pane.gridvisible = false;\n        redraw = true;\n        pane.gridvisible = true;\n    }\n\n    if (redraw) {\n        if (typeof pane.gridgraphics != 'undefined') {\n            pane.container.removeChild(pane.gridgraphics);\n        }\n\n        pane.gridgraphics = new PIXI.Graphics();\n        let gridsizex = g_ctx.tiledimx;\n        let gridsizey = g_ctx.tiledimy;\n        pane.gridgraphics.lineStyle(1, 0x000000, 1);\n\n\n        let index = 0;\n        for (let i = 0; i < pane.widthpx; i += gridsizex) {\n            pane.gridgraphics.moveTo(i + pane.fudgex, 0 + pane.fudgey);\n            pane.gridgraphics.lineTo(i + pane.fudgex, pane.heightpx + pane.fudgey);\n            pane.gridgraphics.moveTo(i + gridsizex + pane.fudgex, 0 + pane.fudgey);\n            pane.gridgraphics.lineTo(i + gridsizex + pane.fudgex, pane.heightpx + pane.fudgey);\n\n        }\n        for (let j = 0; j < pane.heightpx; j += gridsizey) {\n            pane.gridgraphics.moveTo(0 + pane.fudgex, j + gridsizey + pane.fudgey);\n            pane.gridgraphics.lineTo(pane.widthpx + pane.fudgex, j + gridsizey + pane.fudgey);\n            pane.gridgraphics.moveTo(0 + pane.fudgex, j + pane.fudgey);\n            pane.gridgraphics.lineTo(pane.heightpx + pane.fudgex, j + pane.fudgey);\n        }\n\n        if(pane.gridvisible){\n            pane.container.addChild(pane.gridgraphics);\n        }\n        return;\n    }\n\n    if (pane.gridtoggle) {\n        pane.container.addChild(pane.gridgraphics);\n        pane.gridvisible = true;\n    }else{\n        pane.container.removeChild(pane.gridgraphics);\n        pane.gridvisible = false;\n    }\n\n    pane.gridtoggle = !pane.gridtoggle;\n}\n\n\n// --\n// Variable placement logic Level1\n// --\n\nfunction centerCompositePane(x, y){\n    var compositepane = document.getElementById(\"compositepane\");\n    compositepane.scrollLeft = x - (CONFIG.htmlCompositePaneW/2);\n    compositepane.scrollTop  = y - (CONFIG.htmlCompositePaneH/2);\n}\n\nfunction getOldTileValue(layer, x, y) {\n    let levelIndex = level_index_from_px(x, y);\n    return layer.sprites[levelIndex] ? layer.sprites[levelIndex].index : -1;\n}\n\nfunction centerLayerPanes(x, y){\n    // TODO remove magic number pulled from index.html\n    g_ctx.g_layers.map((l) => {\n        l.scrollpane.scrollLeft = x - (CONFIG.htmlLayerPaneW/2);\n        l.scrollpane.scrollTop  = y - (CONFIG.htmlLayerPaneH/2);\n      });\n}\n\nfunction onLevelMouseover(e) {\n    let x = e.data.global.x;\n    let y = e.data.global.y;\n    if(g_ctx.debug_flag2){\n        console.log(\"onLevelMouseOver \",this.num);\n    }\n    if (x < this.scrollpane.scrollLeft || x > this.scrollpane.scrollLeft + CONFIG.htmlCompositePaneW) {\n        return;\n    }\n    if (y < this.scrollpane.scrollTop || y > this.scrollpane.scrollTop + CONFIG.htmlCompositePaneH) {\n        return;\n    }\n\n    // FIXME test code\n    if ( g_ctx.spritesheet != null){\n        let ctile  =  new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']);\n        let ctile2 =  new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']);\n        ctile.animationSpeed = .1;\n        ctile2.animationSpeed = .1;\n        ctile.autoUpdate = true;\n        ctile2.autoUpdate = true;\n        ctile.alpha = .5;\n        ctile2.alpha = .5;\n        ctile.play();\n        ctile2.play();\n\n        this.mouseshadow.addChild(ctile);\n        g_ctx.composite.mouseshadow.addChild(ctile2);\n    // FIXME test code\n    }\n    else if (this.lasttileindex != g_ctx.tile_index) {\n        this.mouseshadow.removeChildren(0);\n        g_ctx.composite.mouseshadow.removeChildren(0);\n        if (g_ctx.selected_tiles.length == 0) {\n            let shadowsprite = null;\n            let shadowsprite2 = null;\n\n            let pxloc = tileset_px_from_index(g_ctx.tile_index);\n\n            shadowsprite  = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n            shadowsprite2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n\n            shadowsprite.alpha = .5;\n            shadowsprite2.alpha = .5;\n            this.mouseshadow.addChild(shadowsprite);\n            g_ctx.composite.mouseshadow.addChild(shadowsprite2);\n        } else {\n            // TODO! adjust for fudge\n            for (let i = 0; i < g_ctx.selected_tiles.length; i++) {\n                let tile = g_ctx.selected_tiles[i];\n                let pxloc = tileset_px_from_index(tile[2]);\n\n                const shadowsprite  = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n                const shadowsprite2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n                shadowsprite.x = tile[0] * g_ctx.tiledimx;\n                shadowsprite.y = tile[1] * g_ctx.tiledimx;\n                shadowsprite2.x = tile[0] * g_ctx.tiledimx;\n                shadowsprite2.y = tile[1] * g_ctx.tiledimx;\n                shadowsprite.alpha = .5;\n                shadowsprite2.alpha = .5;\n                this.mouseshadow.addChild(shadowsprite);\n                g_ctx.composite.mouseshadow.addChild(shadowsprite2);\n            }\n\n        }\n        this.mouseshadow.x = x - 16;\n        this.mouseshadow.y = y - 16;\n        this.container.removeChild(this.mouseshadow);\n        g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow);\n        this.container.addChild(this.mouseshadow);\n        g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow);\n    }\n\n    g_ctx.composite.app.stage.removeChild(g_ctx.composite.circle);\n    g_ctx.composite.app.stage.addChild(g_ctx.composite.circle);\n}\n\n\nfunction onLevelMouseOut(e) {\n    if (g_ctx.debug_flag2) {\n        console.log(\"onLevelMouseOut \",this.num);\n    }\n\n    //FIXME there is a funky race condition where the mouse enters a second layer before leaving the last and the following line\n    //deletes the composite mouseshadow. I'm not quite sure how to solve without mapping the composite.mouseshadow to each layer\n\n    this.mouseshadow.removeChildren(0);\n    g_ctx.composite.mouseshadow.removeChildren();\n}\n\nfunction onLevelMousemove(e) {\n    let x = e.data.global.x;\n    let y = e.data.global.y;\n\n    // FIXME TEST CODE\n    this.mouseshadow.x = x-8;\n    this.mouseshadow.y = y-8;\n    g_ctx.composite.mouseshadow.x = x-8;\n    g_ctx.composite.mouseshadow.y = y-8;\n    // FIXME TEST CODE\n\n\n    if (x < this.scrollpane.scrollLeft || x > this.scrollpane.scrollLeft + CONFIG.htmlCompositePaneW) {\n        return;\n    }\n    if (y < this.scrollpane.scrollTop || y > this.scrollpane.scrollTop + CONFIG.htmlCompositePaneH) {\n        return;\n    }\n\n    g_ctx.composite.circle.clear();\n    g_ctx.composite.circle.beginFill(0xe50000, 0.5);\n    g_ctx.composite.circle.drawCircle(e.data.global.x, e.data.global.y, 3);\n    g_ctx.composite.circle.endFill();\n}\nfunction onCompositeMousedown(layer, e) {\n    if (g_ctx.debug_flag) {\n        console.log('onCompositeMouseDown: X', e.data.global.x, 'Y', e.data.global.y);\n    }\n\n    let xorig = e.data.global.x;\n    let yorig = e.data.global.y;\n\n    centerLayerPanes(xorig,yorig);\n}\n\n\n// Place with no variable target at destination\nfunction levelPlaceNoVariable(layer, e) {\n    if (g_ctx.debug_flag) {\n        console.log('levelPlaceNoVariable: X', e.data.global.x, 'Y', e.data.global.y);\n    }\n\n    let xorig = e.data.global.x;\n    let yorig = e.data.global.y;\n\n    centerCompositePane(xorig, yorig);\n\n    if (g_ctx.dkey || g_ctx.selected_tiles.length == 0) {\n        let oldValue = getOldTileValue(layer, e.data.global.x, e.data.global.y);\n        let ti = layer.addTileLevelPx(e.data.global.x, e.data.global.y, g_ctx.tile_index);\n        UNDO.undo_add_single_index_as_task(layer, ti, oldValue);\n    } else {\n        UNDO.undo_mark_task_start(layer);\n        for (let index of g_ctx.selected_tiles) {\n            // Calculate position and get old value\n            let x = xorig + index[0] * g_ctx.tiledimx;\n            let y = yorig + index[1] * g_ctx.tiledimx;\n            let oldValue = getOldTileValue(layer, x, y);\n            \n            let ti = layer.addTileLevelPx(x, y, index[2]);\n            UNDO.undo_add_index_to_task(ti, oldValue);\n        }\n        UNDO.undo_mark_task_end();\n    }\n}\n\n// Listen to pointermove on stage once handle is pressed.\nfunction onLevelPointerDown(layer, e)\n{\n    if (g_ctx.debug_flag) {\n        console.log(\"onLevelPointerDown()\");\n    }\n    layer.app.stage.eventMode = 'static';\n    layer.app.stage.addEventListener('pointermove', onLevelDrag.bind(null, layer, e));\n\n    layer.container.removeChild(layer.mouseshadow);\n    g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow);\n\n    layer.dragctx.startx = e.data.global.x;\n    layer.dragctx.starty = e.data.global.y;\n    layer.dragctx.endx = e.data.global.x;\n    layer.dragctx.endy = e.data.global.y;\n\n    layer.app.stage.addChild(layer.dragctx.square);\n    layer.app.stage.addChild(layer.dragctx.tooltip);\n}\n\nfunction onLevelDrag(layer, e)\n{\n    if(layer.dragctx.startx == -1){\n        layer.dragctx.square.clear();\n        return;\n    }\n\n    layer.dragctx.endx = e.global.x;\n    layer.dragctx.endy = e.global.y;\n\n    if (g_ctx.debug_flag) {\n        console.log(\"onLevelDrag()\");\n    }\n    \n    layer.dragctx.square.clear();\n    layer.dragctx.square.beginFill(0xFF3300, 0.3);\n    layer.dragctx.square.lineStyle(2, 0xffd900, 1);\n    layer.dragctx.square.moveTo(layer.dragctx.startx, layer.dragctx.starty);\n    layer.dragctx.square.lineTo(layer.dragctx.endx, layer.dragctx.starty);\n    layer.dragctx.square.lineTo(layer.dragctx.endx, layer.dragctx.endy);\n    layer.dragctx.square.lineTo(layer.dragctx.startx, layer.dragctx.endy);\n    layer.dragctx.square.closePath();\n    layer.dragctx.square.endFill();\n\n    const vwidth  = Math.floor((layer.dragctx.endx - layer.dragctx.startx)/g_ctx.tiledimx);\n    const vheight = Math.floor((layer.dragctx.endy - layer.dragctx.starty)/g_ctx.tiledimx);\n    layer.dragctx.tooltip.x = e.global.x + 16;\n    layer.dragctx.tooltip.y = e.global.y - 4;\n    layer.dragctx.tooltip.text = \"[\"+vwidth+\",\"+vheight+\"]\\n\"+\n                                 \"(\"+Math.floor(e.global.x/g_ctx.tiledimx)+\",\"+Math.floor(e.global.y/g_ctx.tiledimx)+\")\";\n    //layer.dragctx.tooltip.text = \"(\"+e.global.x+\",\"+e.global.y+\")\";\n}\n\n// Stop dragging feedback once the handle is released.\nfunction onLevelDragEnd(layer, e)\n{\n    layer.dragctx.endx = e.data.global.x;\n    layer.dragctx.endy = e.data.global.y;\n\n    if(layer.dragctx.startx == -1){\n        console.log(\"onLevelDragEnd() start is -1 bailing\");\n        return;\n    }\n    if (g_ctx.debug_flag) {\n        console.log(\"onLevelDragEnd()\");\n    }\n\n    if(layer.dragctx.endx < layer.dragctx.startx){\n        let tmp = layer.dragctx.endx;\n        layer.dragctx.endx = layer.dragctx.startx;\n        layer.dragctx.startx = tmp;\n    }\n    if(layer.dragctx.endy < layer.dragctx.starty){\n        let tmp = layer.dragctx.endy;\n        layer.dragctx.endy = layer.dragctx.starty;\n        layer.dragctx.starty = tmp;\n    }\n\n    //FIXME TEST CODE show mouseshadow again once done draggin\n    layer.container.addChild(layer.mouseshadow);\n    g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow);\n\n    layer.app.stage.eventMode = 'auto';\n    layer.app.stage.removeChild(layer.dragctx.square);\n    layer.app.stage.removeChild(layer.dragctx.tooltip);\n\n    let starttilex = Math.floor(layer.dragctx.startx / g_ctx.tiledimx);\n    let starttiley = Math.floor(layer.dragctx.starty / g_ctx.tiledimx);\n    let endtilex = Math.floor(layer.dragctx.endx / g_ctx.tiledimx);\n    let endtiley = Math.floor(layer.dragctx.endy / g_ctx.tiledimx);\n\n    if (g_ctx.debug_flag) {\n        console.log(\"sx \", starttilex, \" ex \", endtilex);\n        console.log(\"sy \", starttiley, \" ey \", endtiley);\n    }\n\n    // no variable placement. \n    if(starttilex === endtilex && starttiley == endtiley ){\n        levelPlaceNoVariable(layer, e);\n        layer.dragctx.startx = -1;\n        layer.dragctx.endx    = -1;\n        layer.dragctx.starty = -1;\n        layer.dragctx.endy    = -1;\n        return;\n    }\n\n    if (g_ctx.selected_tiles.length == 0) {\n        UNDO.undo_mark_task_start(layer);\n        for (let i = starttilex; i <= endtilex; i++) {\n            for (let j = starttiley; j <= endtiley; j++) {\n                let x = i * g_ctx.tiledimx;\n                let y = j * g_ctx.tiledimx;\n                let oldValue = getOldTileValue(layer, x, y);\n                let ti = layer.addTileLevelPx(x, y, g_ctx.tile_index);\n                UNDO.undo_add_index_to_task(ti, oldValue);\n            }\n        }\n        UNDO.undo_mark_task_end();\n    } else {\n        // figure out selected grid\n        let selected_grid = Array.from(Array(64), () => new Array(64)); // FIXME ... hope 64x64 is enough\n        let row = 0;\n        let column = 0;\n        let selected_row = g_ctx.selected_tiles[0][1];\n        // selected_grid[0] = [];\n        for (let index of g_ctx.selected_tiles) {\n            // console.log(\"Selected row \", selected_row, index);\n            if(index[1] != selected_row){\n                selected_row = index[1];\n                row++;\n                column = 0;\n                //selected_grid[row] = [];\n            }\n            selected_grid[column++][row]  = index;\n        }\n        // at this point should have a 3D array of the selected tiles and the size should be row, column\n\n        UNDO.undo_mark_task_start(layer);\n\n        let ti=0;\n        for (let i = starttilex; i <= endtilex; i++) {\n            for (let j = starttiley; j <= endtiley; j++) {\n                // Get the old value before placing new tile\n                let x = i * g_ctx.tiledimx;\n                let y = j * g_ctx.tiledimx;\n                let oldValue = getOldTileValue(layer, x, y);\n\n                if (j === starttiley) { // first row \n                    if (i === starttilex) { // top left corner\n                        ti = layer.addTileLevelPx(x, y, selected_grid[0][0][2]);\n                    }\n                    else if (i == endtilex) { // top right corner\n                        ti = layer.addTileLevelPx(x, y, selected_grid[column - 1][0][2]);\n                    } else { // top middle\n                        ti = layer.addTileLevelPx(x, y, selected_grid[1][0][2]);\n                    }\n                } else if (j === endtiley) { // last row\n                    if (i === starttilex) { // bottom left corner\n                        ti = layer.addTileLevelPx(x, y, selected_grid[0][row][2]);\n                    }\n                    else if (i == endtilex) { // bottom right corner\n                        ti = layer.addTileLevelPx(x, y, selected_grid[column - 1][row][2]);\n                    } else { // bottom middle\n                        ti = layer.addTileLevelPx(x, y, selected_grid[1][row][2]);\n                    }\n                } else { // middle row\n                    if (i === starttilex) { // middle left \n                        ti = layer.addTileLevelPx(x, y, selected_grid[0][(row > 0)? 1 : 0][2]);\n                    }\n                    else if (i === endtilex) { // middle end \n                        ti = layer.addTileLevelPx(x, y, selected_grid[column - 1][(row > 0)? 1 : 0][2]);\n                    } else { // middle middle\n                        ti = layer.addTileLevelPx(x, y, selected_grid[1][(row > 0)? 1 : 0][2]);\n                    }\n                }\n                UNDO.undo_add_index_to_task(ti, oldValue);\n            }\n        }\n        UNDO.undo_mark_task_end();\n    }\n\n    layer.dragctx.square.clear();\n\n    layer.dragctx.startx = -1;\n    layer.dragctx.starty = -1;\n}\n\n\n\n// --\n// Initialized all pixi apps / components for application\n// --\nfunction initPixiApps() {\n\n    // -- Editor wide globals --\n\n    // First layer of level\n    const level_app0 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level0') });\n    let layer0 = new LayerContext(level_app0, document.getElementById(\"layer0pane\"), 0);\n\n    // second layer of level \n    const level_app1 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level1') });\n    let layer1 = new LayerContext(level_app1, document.getElementById(\"layer1pane\"), 1);\n\n    //  object layer of level\n    const level_app2 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level3') });\n    let layer2 = new LayerContext(level_app2, document.getElementById(\"layer2pane\"), 2);\n\n    //  object layer of level\n    const level_app3 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level4') });\n\n    let layer3 = new LayerContext(level_app3, document.getElementById(\"layer3pane\"), 3);\n\n    g_ctx.g_layer_apps = [];\n    g_ctx.g_layer_apps.push(level_app0 );\n    g_ctx.g_layer_apps.push(level_app1);\n    g_ctx.g_layer_apps.push(level_app2);\n    g_ctx.g_layer_apps.push(level_app3);\n\n\n    g_ctx.g_layers = [];\n    g_ctx.g_layers.push(layer0);\n    g_ctx.g_layers.push(layer1);\n    g_ctx.g_layers.push(layer2);\n    g_ctx.g_layers.push(layer3);\n\n    // g_ctx.composite view \n    g_ctx.composite_app = new PIXI.Application({ backgroundAlpha: 0, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('composite') });\n    g_ctx.composite = new CompositeContext(g_ctx.composite_app);\n\n    //  map tab \n    g_ctx.map_app = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('mapcanvas') });\n\n    // g_ctx.tileset\n    g_ctx.tileset_app = new PIXI.Application({ width: 5632 , height: 8672, view: document.getElementById('tileset') });\n    //g_ctx.tileset_app = new PIXI.Application({ width: g_ctx.tilesetpxw, height: g_ctx.tilesetpxh, view: document.getElementById('tileset') });\n    // const { renderer } = g_ctx.tileset_app;\n    // // Install the EventSystem\n    // renderer.addSystem(EventSystem, 'tileevents');\n    g_ctx.tileset = new TilesetContext(g_ctx.tileset_app);\n}\n\nfunction setGridSize(size) {\n    if (size == 16) {\n        if (g_ctx.tiledimx == 16) { return; }\n        g_ctx.tilesettilew = (g_ctx.tilesettilew/ (size / g_ctx.tiledimx));\n        g_ctx.tilesettileh = (g_ctx.tilesettileh / (size / g_ctx.tiledimy));\n        g_ctx.tiledimx = 16;\n        g_ctx.tiledimy = 16;\n        g_ctx.curtiles = g_ctx.tiles16;\n        console.log(\"set to curTiles16\");\n    } else if (size == 32) {\n        if (g_ctx.tiledimx == 32) { return; }\n        g_ctx.tilesettilew = (g_ctx.tilesettilew/ (size / g_ctx.tiledimx));\n        g_ctx.tilesettileh = (g_ctx.tilesettileh / (size / g_ctx.tiledimy));\n        g_ctx.tiledimx = 32;\n        g_ctx.tiledimy = 32;\n        g_ctx.curtiles = g_ctx.tiles32;\n        console.log(\"set to curTiles32\");\n    } else {\n        console.debug(\"Invalid TileDim!\");\n        return;\n    }\n    g_ctx.g_layers.map((l) => redrawGrid (l, true) );\n    redrawGrid(g_ctx.tileset, true);\n    redrawGrid(g_ctx.composite, true);\n}\n\nfunction initRadios() {\n    var rad = document.myForm.radioTiledim;\n    var prev = null;\n    for (var i = 0; i < rad.length; i++) {\n        rad[i].addEventListener('change', function () {\n            if (this !== prev) {\n                prev = this;\n            }\n            setGridSize(this.value);\n        });\n    }\n}\n\n// --\n// Load in default tileset and use to set properties\n// --\n\nfunction initTilesSync(callme) {\n    return new Promise((resolve, reject) => {\n\n        console.log(\"initTileSync\");\n        const texture = new PIXI.BaseTexture(g_ctx.tilesetpath);\n        if(texture.valid) {\n            console.log(\"BaseTexture already valid\");\n            callme();\n            return;\n        }\n\n        console.log(\"Loading texture \", g_ctx.tilesetpath);\n        texture.on('loaded', function () {\n            // size of g_ctx.tileset in px\n            g_ctx.tilesetpxw = texture.width;\n            g_ctx.tilesetpxh = texture.height;\n            console.log(\"Texture size w:\", g_ctx.tilesetpxw, \"h:\", g_ctx.tilesetpxh);\n            // size of g_ctx.tileset in tiles\n            let tileandpad = g_ctx.tiledimx + CONFIG.tilesetpadding;\n            let numtilesandpadw = Math.floor(g_ctx.tilesetpxw / tileandpad);\n            g_ctx.tilesettilew = numtilesandpadw + Math.floor((g_ctx.tilesetpxw - (numtilesandpadw * tileandpad)) / g_ctx.tiledimx);\n            let numtilesandpadh = Math.floor(g_ctx.tilesetpxh / tileandpad);\n            g_ctx.tilesettileh = numtilesandpadh + Math.floor((g_ctx.tilesetpxh - (numtilesandpadh * tileandpad)) / g_ctx.tiledimx);\n            console.log(\"Number of x tiles \", g_ctx.tilesettilew, \" y tiles \", g_ctx.tilesettileh);\n            g_ctx.MAXTILEINDEX = g_ctx.tilesettilew * g_ctx.tilesettileh;\n\n            texture.destroy();\n            resolve();\n            callme();\n        });\n\n    });\n}\n\n// --\n// Load default Tileset\n// --\n\nconst initTilesConfig = async () => {\n\n    g_ctx.tilesetpath = CONFIG.DEFAULTTILESETPATH;\n\n    return new Promise((resolve, reject) => {\n        \n    const texture = new PIXI.BaseTexture(g_ctx.tilesetpath);\n    if (g_ctx.debug_flag) {\n        console.log(\"initTilessConfi: Loading texture \",g_ctx.tilesetpath);\n    }\n    texture .on('loaded', function() {\n        // size of g_ctx.tileset in px\n        g_ctx.tilesetpxw = texture.width;\n        g_ctx.tilesetpxh = texture.height;\n        if (g_ctx.debug_flag) {\n            console.log(\"\\tsize w:\", g_ctx.tilesetpxw, \"h:\", g_ctx.tilesetpxh);\n        }\n\n        // size of g_ctx.tileset in tiles\n        let tileandpad = g_ctx.tiledimx + CONFIG.tilesetpadding;\n        let numtilesandpadw = Math.floor(g_ctx.tilesetpxw / tileandpad);\n        g_ctx.tilesettilew = numtilesandpadw + Math.floor((g_ctx.tilesetpxw - (numtilesandpadw * tileandpad))/g_ctx.tiledimx);\n        let numtilesandpadh = Math.floor(g_ctx.tilesetpxh / tileandpad);\n        g_ctx.tilesettileh = numtilesandpadh + Math.floor((g_ctx.tilesetpxh - (numtilesandpadh * tileandpad))/g_ctx.tiledimx);\n\n        if (g_ctx.debug_flag) {\n            console.log(\"\\tnum tiles x \", g_ctx.tilesettilew, \" y \", g_ctx.tilesettileh);\n        }\n\n        g_ctx.MAXTILEINDEX = g_ctx.tilesettilew * g_ctx.tilesettileh;\n\n        texture.destroy();\n        resolve();\n    });\n\n  \n      });\n  };\n\nfunction initTiles() {\n    // load g_ctx.tileset into a global array of textures for blitting onto levels\n    const bt = PIXI.BaseTexture.from(g_ctx.tilesetpath, {\n        scaleMode: PIXI.SCALE_MODES.NEAREST,\n    });\n    for (let x = 0; x < CONFIG.tilesettilewidth; x++) {\n        for (let y = 0; y < CONFIG.tilesettileheight; y++) {\n            g_ctx.tiles32[x + y * CONFIG.tilesettilewidth] = new PIXI.Texture(\n                bt,\n                new PIXI.Rectangle(x * 32, y * 32, 32, 32),\n            );\n        }\n    }\n    for (let x = 0; x < CONFIG.tilesettilewidth * 2; x++) {\n        for (let y = 0; y < CONFIG.tilesettileheight * 2; y++) {\n            g_ctx.tiles16[x + y * CONFIG.tilesettilewidth * 2] = new PIXI.Texture(\n                bt,\n                new PIXI.Rectangle(x * 16, y * 16, 16, 16),\n            );\n        }\n    }\n\n    g_ctx.curtiles = g_ctx.tiles32;\n}\n\nasync function init() {\n\n    UI.initMainHTMLWindow();\n\n    // We need to load the Tileset to know how to size things. So we block until done. \n    await initTilesConfig(); \n\n    initPixiApps();\n    initRadios();\n    initTiles();\n\n    UI.initLevelLoader(loadMapFromModule);\n    UI.initCompositePNGLoader();\n    UI.initSpriteSheetLoader();\n    UI.initTilesetLoader( loadMapFromModule.bind(null, g_ctx));\n}\n\ninit();"
  },
  {
    "path": "src/editor/leconfig.js",
    "content": "export const DEFAULTTILESETPATH = \"./tilesets/gentle.png\";\n//export const DEFAULTTILESETPATH = \"./tilesets/magecity.png\";\n//export const DEFAULTTILESETPATH = \"./tilesets/forest.png\";\n//export const DEFAULTTILESETPATH = \"./tilesets/Serene.png\";\n//export const DEFAULTTILESETPATH = \"./tilesets/gentletreewall.png\";\n//export const DEFAULTTILESETPATH = \"./tilesets/Modern.png\";\n//export const DEFAULTTILESETPATH = \"./tilesets/phantasy2.png\";\n\nexport const tilesetpadding = 0; \n\n\nexport const DEFAULTILEDIMX = 32; // px\nexport const DEFAULTILEDIMY = 32; // px\n\nexport const levelwidth  = 2048; // px\nexport const levelheight = 1536; // px\n\nexport let leveltilewidth  = Math.floor(levelwidth / DEFAULTILEDIMX);\nexport let leveltileheight = Math.floor(levelheight / DEFAULTILEDIMX);\n\nexport const MAXTILEINDEX = leveltilewidth * leveltileheight;\n\n\n// -- HTML\n\nexport const htmlLayerPaneW = 800;\nexport const htmlLayerPaneH = 600\n\nexport const htmlTilesetPaneW = 800;\nexport const htmlTilesetPaneH = 600;\n\nexport const htmlCompositePaneW = 800;\nexport const htmlCompositePaneH = 600;\n\n// --  zIndex\n\n// 1-10 taken by layers\nexport const zIndexFilter           =  20;\nexport const zIndexMouseShadow      =  30;\nexport const zIndexGrid             =  50;\nexport const zIndexCompositePointer =  100;\n"
  },
  {
    "path": "src/editor/lecontext.js",
    "content": "import * as PIXI from 'pixi.js'\nimport * as CONFIG from './leconfig.js'\n\nvar ContextCreate = (function(){\n\n    function ContextSingleton() {\n        this.tilesetpxw = 0;\n        this.tilesetpxh = 0;\n        this.tilesettilew = 0;\n        this.tilesettileh = 0;\n        this.MAXTILEINDEX = 0;\n        this.tile_index = 0;\n        this.selected_tiles = []; // current set of selected tiles\n        this.spritesheet = null; // loaded spritesheet\n        this.tiledimx = CONFIG.DEFAULTILEDIMX ; // px\n        this.tiledimy = CONFIG.DEFAULTILEDIMY; // px\n        this.dimlog = Math.log2(this.tileDim);  //log2(TileDim)\n        this.dkey = false;   // is 'd' key depressed? (for delete)\n        this.tiles32  = [];  // all tiles from tilemap (32x32)\n        this.tiles16  = []; \n        this.fudgetiles = [];\n        this.g_layers = []; // level layers\n\n    }\n\n    var instance;\n    return {\n        getInstance: function(){\n            if (instance == null) {\n                instance = new ContextSingleton();\n                // Hide the constructor so the returned object can't be new'd...\n                instance.constructor = null;\n            }\n            return instance;\n        }\n   };\n})();\n\n// global shared state between all panes\nexport let g_ctx = ContextCreate.getInstance();"
  },
  {
    "path": "src/editor/lehtmlui.js",
    "content": "import * as PIXI from 'pixi.js'\nimport { g_ctx }  from './lecontext.js' // global context\nimport * as CONFIG from './leconfig.js' \n\n// --\n//  Set sizes and limits for HTML in main UI\n// --\n\nexport function initMainHTMLWindow() {\n    document.getElementById(\"layer0pane\").style.maxWidth  = \"\"+CONFIG.htmlLayerPaneW+\"px\"; \n    document.getElementById(\"layer0pane\").style.maxHeight = \"\"+CONFIG.htmlLayerPaneH+\"px\"; \n    document.getElementById(\"layer1pane\").style.maxWidth  = \"\"+CONFIG.htmlLayerPaneW+\"px\"; \n    document.getElementById(\"layer1pane\").style.maxHeight = \"\"+CONFIG.htmlLayerPaneH+\"px\"; \n    document.getElementById(\"layer2pane\").style.maxWidth  = \"\"+CONFIG.htmlLayerPaneW+\"px\"; \n    document.getElementById(\"layer2pane\").style.maxHeight = \"\"+CONFIG.htmlLayerPaneH+\"px\"; \n    document.getElementById(\"layer3pane\").style.maxWidth  = \"\"+CONFIG.htmlLayerPaneW+\"px\"; \n    document.getElementById(\"layer3pane\").style.maxHeight = \"\"+CONFIG.htmlLayerPaneH+\"px\"; \n\n    document.getElementById(\"tilesetpane\").style.maxWidth  = \"\"+CONFIG.htmlTilesetPaneW+\"px\"; \n    document.getElementById(\"tilesetpane\").style.maxHeight = \"\"+CONFIG.htmlTilesetPaneH+\"px\";\n    document.getElementById(\"compositepane\").style.maxWidth  = \"\"+CONFIG.htmlCompositePaneW+\"px\"; \n    document.getElementById(\"compositepane\").style.maxHeight = \"\"+CONFIG.htmlCompositePaneH+\"px\";\n\n    // hide map tab\n    let mappane = document.getElementById(\"map\");\n    mappane.style.display = \"none\";\n}\n\n// --\n// Initialize handlers for file loading\n// --\n\n\n\n\n\n// --\n// Initialize handlers loading a PNG file into the composite window \n// --\n\nexport function initCompositePNGLoader() {\n    const fileInput = document.getElementById('compositepng');\n    fileInput.onchange = (evt) => {\n        if (!window.FileReader) return; // Browser is not compatible\n        if (g_ctx.debug_flag) {\n            console.log(\"compositepng \", fileInput.files[0].name);\n        }\n        let bgname = fileInput.files[0].name;\n\n        const texture = PIXI.Texture.from(\"./tilesets/\"+bgname);\n        const bg      = new PIXI.Sprite(texture);\n        bg.zIndex = 0;\n        g_ctx.composite.container.addChild(bg);\n    }\n}\n\n// -- \n// initailized handler to load a spriteSheet into current working tile\n// --\n\nexport function initSpriteSheetLoader() {\n    const fileInput = document.getElementById('spritesheet');\n    fileInput.onchange = async (evt) => {\n        if (!window.FileReader) return; // Browser is not compatible\n        if (g_ctx.debug_flag) {\n            console.log(\"spritesheet \", fileInput.files[0].name);\n        }\n        let ssname = fileInput.files[0].name;\n\n        let sheet = await PIXI.Assets.load(\"./\"+ssname);\n        console.log(sheet);\n        g_ctx.tileset.addTileSheet(ssname, sheet);\n        g_ctx.selected_tiles = [];\n    }\n}\n\n// -- \n// initailized handler to load a new tileset \n// --\n\nexport function initTilesetLoader(callme) {\n    const fileInput = document.getElementById('tilesetfile');\n    fileInput.onchange = async (evt) => {\n        if (!window.FileReader) return; // Browser is not compatible\n        if (g_ctx.debug_flag) {\n            console.log(\"tilesetfile \", fileInput.files[0].name);\n        }\n        g_ctx.tilesetpath =  \"./tilesets/\"+fileInput.files[0].name;\n\n        callme();\n    }\n}\n\n\n// -- \n// initailized handler to load a level from a file \n// --\n\nfunction doimport (str) {\n    if (globalThis.URL.createObjectURL) {\n      const blob = new Blob([str], { type: 'text/javascript' })\n      const url = URL.createObjectURL(blob)\n      const module = import(url)\n      URL.revokeObjectURL(url) // GC objectURLs\n      return module\n    }\n    \n    const url = \"data:text/javascript;base64,\" + btoa(moduleData)\n    return import(url)\n  }\n\nexport function initLevelLoader(callme) {\n    let filecontent = \"\";\n\n    const fileInput = document.getElementById('levelfile');\n    fileInput.onchange = (evt) => {\n        if (!window.FileReader) return; // Browser is not compatible\n\n        var reader = new FileReader();\n\n        reader.onload = function (evt) {\n            if (evt.target.readyState != 2) return;\n            if (evt.target.error) {\n                alert('Error while reading file');\n                return;\n            }\n\n            filecontent = evt.target.result;\n            doimport(filecontent).then(mod => callme(mod));\n        };\n\n        reader.readAsText(evt.target.files[0]);\n    }\n}"
  },
  {
    "path": "src/editor/mapfile.js",
    "content": "import * as CONFIG from './leconfig.js' \nimport * as UTIL   from './eutils.js'\nimport { g_ctx }  from './lecontext.js' // global context\n\n\n\nfunction generate_preamble() {\n    const mapfile_preamble = '' +\n        '// Map generated by assettool.js [' + new Date() + ']\\n' +\n        '\\n' +\n        'export const tilesetpath = \"' + g_ctx.tilesetpath + '\"\\n' +\n        'export const tiledim = ' + g_ctx.tiledimx + '\\n' +\n        'export const screenxtiles = ' + g_ctx.tilesettilew + '\\n' +\n        'export const screenytiles = ' + g_ctx.tilesettileh + '\\n' +\n        'export const tilesetpxw = ' + g_ctx.tilesetpxw + '\\n' +\n        'export const tilesetpxh = ' + g_ctx.tilesetpxh + '\\n\\n'\n\n       return mapfile_preamble; \n}\n\nconst bgtile_string_start = '' +\n    'export const bgtiles = [\\n' +\n    '   [\\n'\n\nfunction write_map_file(bg_tiles_0, bg_tiles_1, obj_tiles_1, obj_tiles_2, animated_tiles){\n    let text = generate_preamble(); \n    text += bgtile_string_start;\n\n    for(let row = 0; row < bg_tiles_0.length; row++) {\n        text += '[ ';\n        for(let column = 0; column < bg_tiles_0[row].length; column++) {\n            text += bg_tiles_0[row][column];\n            if (column != bg_tiles_0.length - 1){\n                text += ' , ';\n            }\n        }\n        text += '],\\n';\n    }\n    text += '],\\n';\n    text += '[\\n';\n    for(let row = 0; row < bg_tiles_1.length; row++) {\n        text += '[ ';\n        for(let column = 0; column < bg_tiles_1[row].length; column++) {\n            text += bg_tiles_1[row][column];\n            if (column != bg_tiles_1.length - 1){\n                text += ' , ';\n            }\n        }\n        text += '],\\n';\n    }\n    text += '],];\\n\\n';\n\n    text += ''+\n    'export const objmap = [\\n'+\n    '[\\n';\n\n    for(let row = 0; row < obj_tiles_1.length; row++) {\n        text += '[ ';\n        for(let column = 0; column < obj_tiles_1[row].length; column++) {\n            text += obj_tiles_1[row][column];\n            if (column != obj_tiles_1.length - 1){\n                text += ' , ';\n            }\n        }\n        text += '],\\n';\n    }\n    text += '],\\n';\n    text += '[\\n';\n\n    for(let row = 0; row < obj_tiles_2.length; row++) {\n        text += '[ ';\n        for(let column = 0; column < obj_tiles_2[row].length; column++) {\n            text += obj_tiles_2[row][column];\n            if (column != obj_tiles_2.length - 1){\n                text += ' , ';\n            }\n        }\n        text += '],\\n';\n    }\n    text += '],];\\n';\n\n\n    text += ''+\n    'export const animatedsprites = [\\n';\n\n    for(let x = 0 ; x < animated_tiles.length; x++){\n        let atile = animated_tiles[x];\n        text += '{ x: '+atile.x+\", y: \"+ atile.y+ \", w: \"+ atile.width+ \", h: \"+ atile.height ; \n        text += ', layer: '+atile.layer;\n        text += ', sheet: \"'+ atile.spritesheetname+ '\", animation: \"'+ atile.animationname+'\" },\\n';\n    }\n\n    text += '];\\n\\n';\n    text += 'export const mapwidth = bgtiles[0][0].length;\\n';\n    text += 'export const mapheight = bgtiles[0].length;\\n';\n\n    UTIL.download(text, \"map.js\", \"text/plain\");\n}\n\n\nexport function generate_level_file() {\n    let layer0 = g_ctx.g_layers[0];\n    let layer1 = g_ctx.g_layers[1];\n    let layer2 = g_ctx.g_layers[2];\n    let layer3 = g_ctx.g_layers[3];\n\n    let animated_tiles = [];\n\n    // level0 \n    var tile_array0 = Array.from(Array(CONFIG.leveltilewidth), () => new Array(CONFIG.leveltileheight));\n    for (let x = 0; x < CONFIG.leveltilewidth; x++) {\n        for (let y = 0; y < CONFIG.leveltileheight; y++) {\n            tile_array0[x][y] = -1;\n        }\n    }\n    for (var i = 0; i < layer0.container.children.length; i++) {\n        var child = layer0.container.children[i];\n\n        // check if it's an animated sprite\n        if(child.hasOwnProperty('animationSpeed')){\n            child.layer = 0;\n            animated_tiles.push(child);\n            continue;\n        }\n\n        if (!child.hasOwnProperty('index')) {\n            continue;\n        }\n        let x_coord = child.x / g_ctx.tiledimx;\n        let y_coord = child.y / g_ctx.tiledimy;\n\n        if (typeof tile_array0[x_coord] == 'undefined'){\n            console.log(\"**Error xcoord undefined \", x_coord);\n\n        }\n        else if (typeof tile_array0[x_coord][y_coord] == 'undefined'){\n            console.log(\"**Error xcoord/ycoord undefined \", x_coord, y_coord);\n        }else{\n            tile_array0[x_coord][y_coord] = child.index;\n        }\n    }\n\n    // level1 \n    var tile_array1 = Array.from(Array(CONFIG.leveltilewidth), () => new Array(CONFIG.leveltileheight));\n    for (let x = 0; x < CONFIG.leveltilewidth; x++) {\n        for (let y = 0; y < CONFIG.leveltileheight; y++) {\n            tile_array1[x][y] = -1;\n        }\n    }\n    for (var i = 0; i < layer1.container.children.length; i++) {\n        var child = layer1.container.children[i];\n\n        // check if it's an animated sprite\n        if(child.hasOwnProperty('animationSpeed')){\n            child.layer = 1;\n            animated_tiles.push(child);\n            continue;\n        }\n\n        if (!child.hasOwnProperty('index')) {\n            continue;\n        }\n        let x_coord = child.x / g_ctx.tiledimx;\n        let y_coord = child.y / g_ctx.tiledimy;\n        if (typeof tile_array1[x_coord] == 'undefined'){\n            console.log(\"**Error xcoord undefined \", x_coord);\n\n        }\n        else if (typeof tile_array1[x_coord][y_coord] == 'undefined'){\n            console.log(\"**Error xcoord/ycoord undefined \", x_coord, y_coord);\n        }else{\n            tile_array1[x_coord][y_coord] = child.index;\n        }\n    }\n\n    //  object level\n    var tile_array2 = Array.from(Array(CONFIG.leveltilewidth), () => new Array(CONFIG.leveltileheight));\n    for (let x = 0; x < CONFIG.leveltilewidth; x++) {\n        for (let y = 0; y < CONFIG.leveltileheight; y++) {\n            tile_array2[x][y] = -1;\n        }\n    }\n    for (var i = 0; i < layer2.container.children.length; i++) {\n        var child = layer2.container.children[i];\n\n        // check if it's an animated sprite\n        if(child.hasOwnProperty('animationSpeed')){\n            child.layer = 2;\n            animated_tiles.push(child);\n            continue;\n\n        }\n\n        if (!child.hasOwnProperty('index')) {\n            continue;\n        }\n        let x_coord = child.x / g_ctx.tiledimx;\n        let y_coord = child.y / g_ctx.tiledimy;\n        if (typeof tile_array2[x_coord] == 'undefined'){\n            console.log(\"**Error xcoord undefined \", x_coord);\n\n        }\n        else if (typeof tile_array2[x_coord][y_coord] == 'undefined'){\n            console.log(\"**Error xcoord/ycoord undefined \", x_coord, y_coord);\n        }else{\n        tile_array2[x_coord][y_coord] = child.index;\n        }\n    }\n\n    //  object level\n    var tile_array3 = Array.from(Array(CONFIG.leveltilewidth), () => new Array(CONFIG.leveltileheight));\n    for (let x = 0; x < CONFIG.leveltilewidth; x++) {\n        for (let y = 0; y < CONFIG.leveltileheight; y++) {\n            tile_array3[x][y] = -1;\n        }\n    }\n    for (var i = 0; i < layer3.container.children.length; i++) {\n        var child = layer3.container.children[i];\n\n        // check if it's an animated sprite\n        if(child.hasOwnProperty('animationSpeed')){\n            child.layer = 3;\n            animated_tiles.push(child);\n            continue;\n\n        }\n\n        if (!child.hasOwnProperty('index')) {\n            continue;\n        }\n        let x_coord = child.x / g_ctx.tiledimx;\n        let y_coord = child.y / g_ctx.tiledimy;\n        if (typeof tile_array3[x_coord] == 'undefined'){\n            console.log(\"**Error xcoord undefined \", x_coord);\n\n        }\n        else if (typeof tile_array3[x_coord][y_coord] == 'undefined'){\n            console.log(\"**Error xcoord/ycoord undefined \", x_coord, y_coord);\n        }else{\n        tile_array3[x_coord][y_coord] = child.index;\n        }\n    }\n\n\n    write_map_file(tile_array0, tile_array1, tile_array2, tile_array3, animated_tiles);\n}"
  },
  {
    "path": "src/editor/maps/gentle-full.js",
    "content": "// Map generated by assettool.js [Mon Oct 02 2023 00:20:59 GMT-0700 (Pacific Daylight Time)]\n\nexport const tilesetpath = \"./tilesets/gentle-obj.png\"\nexport const tiledim = 32\nexport const screenxtiles = 45\nexport const screenytiles = 32\nexport const tilesetpxw = 1440\nexport const tilesetpxh = 1024\n\nexport const bgtiles = [\n   [\n[ 732 , 777 , 822 , 867 , 912 , 957 , 912 , 957 , 1002 , 1047 , 912 , 957 , 1002 , 1047 , 912 , 957 , 1002 , 1047 , 1001 , 1046 , 946 , 991 , 1035 , 731 , 776 , 821 , 866 , 911 , 956 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 594 , 639 , 684 , 271 , ],\n[ 733 , 778 , 823 , 868 , 913 , 958 , 913 , 958 , 1003 , 1048 , 913 , 958 , 1003 , 1048 , 913 , 958 , 1003 , 1048 , 856 , 901 , 946 , 991 , 1036 , 732 , 777 , 822 , 867 , 912 , 957 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 595 , 640 , 685 , 271 , ],\n[ 734 , 779 , 824 , 869 , 914 , 959 , 914 , 959 , 1004 , 1049 , 914 , 959 , 1004 , 1049 , 914 , 959 , 1004 , 1049 , 857 , 902 , 947 , 992 , 1037 , 733 , 778 , 823 , 868 , 913 , 958 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 594 , 639 , 684 , 271 , ],\n[ 735 , 780 , 825 , 870 , 915 , 960 , 915 , 960 , 1005 , 1050 , 915 , 960 , 1005 , 1050 , 915 , 960 , 1087 , 1132 , 858 , 903 , 948 , 993 , 1038 , 734 , 779 , 824 , 869 , 914 , 959 , 271 , 271 , 180 , 225 , 225 , 315 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 595 , 640 , 685 , 271 , ],\n[ 736 , 781 , 826 , 871 , 916 , 278 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 859 , 904 , 949 , 994 , 1039 , 735 , 780 , 825 , 870 , 915 , 960 , 271 , 271 , 181 , 226 , 226 , 140 , 270 , 315 , 271 , 271 , 271 , 271 , 271 , 271 , 541 , 586 , 631 , 686 , 271 , ],\n[ 737 , 782 , 827 , 872 , 917 , 233 , 271 , 271 , 271 , 271 , 271 , 233 , 271 , 271 , 271 , 271 , 1088 , 1133 , 860 , 905 , 950 , 995 , 1040 , 736 , 781 , 826 , 871 , 916 , 961 , 271 , 271 , 181 , 226 , 278 , 272 , 271 , 316 , 271 , 271 , 271 , 271 , 271 , 551 , 542 , 587 , 632 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 235 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 1089 , 1134 , 861 , 906 , 951 , 996 , 1041 , 737 , 782 , 827 , 872 , 917 , 962 , 271 , 271 , 181 , 226 , 226 , 272 , 271 , 675 , 271 , 271 , 271 , 271 , 272 , 272 , 642 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 235 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 1090 , 1135 , 862 , 907 , 952 , 997 , 1042 , 738 , 783 , 828 , 873 , 918 , 963 , 271 , 271 , 183 , 228 , 228 , 184 , 271 , 675 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 182 , 280 , 317 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 962 , 0 , 135 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 140 , 273 , 318 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 0 , 45 , 90 , 135 , 271 , 271 , 271 , 271 , 962 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 3 , 48 , 93 , 271 , 271 , 271 , 271 , 271 , 962 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 279 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 271 , 962 , 962 , 962 , 962 , 143 , 188 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 278 , 962 , 962 , 962 , 144 , 189 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 271 , 271 , 271 , 271 , 145 , 190 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 3 , 138 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 360 , 405 , 405 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 1270 , 1315 , 1360 , 1405 , 226 , 360 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 409 , 451 , 451 , 451 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 1271 , 1316 , 226 , 226 , 226 , 361 , 409 , 409 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 407 , 452 , 452 , 452 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 450 , 362 , 406 , 407 , 451 , 451 , 271 , 271 , 271 , 405 , 405 , 450 , 320 , 409 , 451 , 452 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 405 , 450 , 405 , 450 , 450 , 320 , 406 , 406 , 451 , 589 , 451 , 451 , 451 , 406 , 451 , 409 , 451 , 409 , 451 , 452 , 406 , 275 , 405 , 450 , 405 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 406 , 406 , 406 , 451 , 451 , 407 , 406 , 407 , 452 , 451 , 451 , 451 , 451 , 407 , 589 , 452 , 452 , 451 , 406 , 451 , 406 , 406 , 407 , 451 , 451 , 275 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 408 , 453 , 408 , 453 , 319 , 406 , 407 , 406 , 451 , 451 , 451 , 589 , 451 , 634 , 452 , 589 , 406 , 451 , 634 , 452 , 274 , 408 , 408 , 319 , 451 , 451 , 451 , 451 , 275 , 405 , 405 , 450 , 405 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 361 , 634 , 452 , 407 , 634 , 451 , 451 , 451 , 451 , 407 , 452 , 452 , 407 , 452 , 452 , 274 , 498 , 271 , 271 , 363 , 408 , 453 , 408 , 453 , 408 , 453 , 319 , 451 , 451 , 275 , 405 , 450 , 405 , 450 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 362 , 409 , 451 , 451 , 451 , 451 , 271 , 271 , 271 , 408 , 408 , 319 , 407 , 452 , 409 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 406 , 451 , 406 , 451 , 451 , 451 , 451 , 544 , 451 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 362 , 407 , 409 , 451 , 451 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 409 , 409 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 544 , 407 , 544 , 589 , 452 , 452 , 634 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 363 , 408 , 453 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 408 , 453 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 319 , 407 , 452 , 452 , 499 , 544 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 319 , 544 , 499 , 499 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 407 , 499 , 499 , 499 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 272 , 271 , 278 , 271 , 271 , 271 , 279 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 544 , 499 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 1129 , 1174 , 1219 , 1264 , 900 , 945 , 990 , 1035 , 1174 , 1219 , 1264 , 1309 , 1354 , 280 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 407 , 499 , 499 , 499 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 721 , 766 , 811 , 856 , 901 , 946 , 991 , 1036 , 1175 , 1220 , 1265 , 1310 , 1355 , 1400 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 499 , 499 , 407 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 722 , 767 , 812 , 857 , 902 , 947 , 992 , 1037 , 1176 , 1221 , 1266 , 1311 , 1356 , 1401 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 544 , 634 , 452 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 723 , 768 , 813 , 858 , 903 , 948 , 993 , 1038 , 1177 , 1222 , 1267 , 1312 , 1357 , 1402 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 452 , 452 , 452 , 274 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 724 , 769 , 814 , 859 , 904 , 949 , 994 , 1039 , 1178 , 1223 , 1268 , 1313 , 1358 , 1403 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 453 , 453 , 453 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 725 , 770 , 815 , 860 , 905 , 950 , 995 , 1040 , 1179 , 1224 , 1269 , 1314 , 1359 , 1404 , 271 , 271 , 6 , 51 , 96 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 726 , 771 , 816 , 861 , 906 , 951 , 996 , 1041 , 1180 , 1225 , 1270 , 1315 , 1360 , 1405 , 271 , 271 , 7 , 52 , 97 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 735 , 780 , 825 , 870 , 907 , 952 , 997 , 1042 , 1181 , 1226 , 1271 , 234 , 1361 , 280 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 227 , 1129 , 1174 , 1219 , 227 , 1309 , 227 , 278 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 962 , 962 , 962 , 233 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 234 , 1130 , 1175 , 1220 , 1265 , 1310 , 1355 , 1400 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 227 , 1131 , 1176 , 1221 , 1266 , 1311 , 1356 , 1401 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 1177 , 1222 , 1267 , 1312 , 1357 , 1402 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 728 , 773 , 818 , 863 , 908 , 953 , 998 , 1043 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 729 , 774 , 819 , 864 , 909 , 954 , 999 , 1044 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 234 , 730 , 775 , 820 , 865 , 910 , 955 , 1000 , 1045 , 271 , 271 , 271 , 280 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 143 , 188 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 731 , 776 , 821 , 866 , 911 , 956 , 1001 , 1046 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 962 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 144 , 189 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1088 , 1133 , 1178 , 1223 , 1268 , 1313 , 1358 , 1403 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 145 , 190 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1089 , 1134 , 1179 , 1224 , 1269 , 1314 , 1359 , 1404 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 271 , 271 , 271 , 271 , 233 , 271 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 280 , 1135 , 1180 , 1225 , 1270 , 1315 , 1360 , 1405 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 271 , 1010 , 1055 , 962 , 962 , 271 , 271 , 271 , 1309 , 1354 , 1399 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 280 , 143 , 188 , 1226 , 1271 , 227 , 1361 , 234 , 271 , 280 , 1129 , 1174 , 1219 , 1264 , ],\n[ 740 , 785 , 830 , 875 , 920 , 965 , 1010 , 740 , 785 , 830 , 875 , 920 , 965 , 1010 , 1310 , 1355 , 1400 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 144 , 189 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1130 , 1175 , 1220 , 1265 , ],\n[ 741 , 786 , 831 , 876 , 921 , 966 , 1011 , 741 , 786 , 831 , 876 , 921 , 966 , 1011 , 1311 , 1356 , 1401 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 145 , 190 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1131 , 1176 , 1221 , 1266 , ],\n[ 742 , 787 , 832 , 877 , 922 , 967 , 1012 , 742 , 787 , 832 , 877 , 922 , 967 , 1012 , 1312 , 1357 , 1402 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 1177 , 1222 , 1267 , ],\n[ 743 , 788 , 833 , 878 , 923 , 968 , 1013 , 743 , 788 , 833 , 878 , 923 , 968 , 1013 , 1313 , 1358 , 1403 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 278 , 271 , 271 , 271 , 271 , 1088 , 1133 , 1178 , 1223 , 1268 , ],\n[ 1178 , 1223 , 1268 , 1223 , 1268 , 1223 , 1268 , 1223 , 1268 , 1223 , 1268 , 1178 , 1223 , 1268 , 1314 , 1359 , 1404 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 227 , 271 , 1134 , 1179 , 1224 , 1269 , ],\n[ 1179 , 1224 , 1269 , 1224 , 1269 , 1224 , 1269 , 1224 , 1269 , 1224 , 1269 , 1179 , 1224 , 1269 , 1315 , 1360 , 1405 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 233 , 271 , 271 , 1143 , 1188 , 1233 , ],\n[ 1180 , 1225 , 1270 , 1225 , 1270 , 1225 , 1270 , 1225 , 1270 , 1225 , 1270 , 1180 , 1225 , 1270 , 1316 , 1361 , 1406 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1009 , 1054 , 1099 , 1144 , 1189 , 1234 , ],\n[ 1181 , 1226 , 1271 , 1226 , 1271 , 1226 , 1271 , 1226 , 1271 , 1226 , 1271 , 1181 , 1226 , 1271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 272 , 1129 , 272 , 965 , 1010 , 1055 , 920 , 965 , 1010 , 1055 , 1100 , 1145 , 1190 , 1235 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 272 , 1130 , 1219 , 966 , 1011 , 1056 , 921 , 966 , 1011 , 1056 , 1101 , 1146 , 1191 , 1236 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1086 , 1131 , 922 , 967 , 1012 , 1057 , 922 , 967 , 1012 , 1057 , 1102 , 1147 , 1192 , 1237 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 923 , 968 , 1013 , 1058 , 923 , 968 , 1013 , 1058 , 1103 , 1148 , 1193 , 1238 , ],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 231 , 276 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 232 , 277 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 416 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 370 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 371 , 416 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 763 , 808 , 754 , 799 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 8 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 417 , 462 , 507 , 552 , 597 , 642 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 764 , 809 , 755 , 800 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 9 , 54 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 844 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 762 , 751 , 796 , 841 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 752 , 797 , 842 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 141 , 186 , -1 , -1 , -1 , -1 , -1 , -1 , 896 , -1 , -1 , 547 , 592 , 637 , 682 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 753 , 798 , 843 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 142 , 187 , -1 , -1 , 11 , -1 , -1 , -1 , 191 , 236 , -1 , 548 , 593 , 638 , 683 , 682 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 11 , 56 , 101 , 146 , 191 , 236 , -1 , 548 , 593 , 638 , 683 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 278 , -1 , -1 , -1 , -1 , -1 , -1 , 12 , 57 , 102 , 147 , 192 , 237 , 941 , -1 , 594 , 639 , 684 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 13 , 58 , 103 , 148 , 193 , 238 , -1 , -1 , 595 , 640 , 685 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1026 , 1071 , 1116 , 1161 , -1 , -1 , -1 , 278 , -1 , -1 , -1 , 15 , 14 , 59 , 104 , 149 , 194 , 239 , 850 , -1 , 594 , 639 , 684 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1027 , 1072 , 1117 , 1162 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 15 , 60 , 105 , 150 , 195 , 850 , -1 , -1 , 595 , 640 , 685 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1028 , 1073 , 1118 , 1163 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 850 , -1 , 551 , 596 , 641 , 686 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1029 , 1074 , 1119 , 1164 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 371 , 416 , -1 , -1 , -1 , -1 , 551 , 596 , 641 , 686 , 687 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 417 , 507 , 462 , 507 , 552 , 597 , 642 , 642 , 687 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 547 , 592 , 637 , 682 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 231 , 276 , -1 , -1 , -1 , 894 , 939 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 548 , 593 , 638 , 683 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 232 , 277 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 549 , 594 , 639 , 684 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 380 , 380 , 425 , 470 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 381 , 381 , 426 , 471 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 382 , 382 , 427 , 472 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 280 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 383 , 428 , 473 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 550 , 595 , 640 , 685 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 384 , 429 , 474 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 551 , 596 , 641 , 686 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 233 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 385 , 385 , 430 , 475 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 462 , 507 , 552 , 552 , 597 , 642 , 687 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 386 , 386 , 431 , 476 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 387 , 387 , 432 , 477 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 11 , 56 , 101 , 146 , 191 , 236 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 279 , -1 , -1 , -1 , -1 , 12 , 57 , 102 , 147 , 192 , 237 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 380 , 425 , 470 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 13 , 58 , 103 , 148 , 193 , 238 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 381 , 426 , 471 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 14 , 59 , 104 , 149 , 194 , 239 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 382 , 427 , 472 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 15 , 60 , 105 , 150 , 195 , 240 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 383 , 428 , 473 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 384 , 429 , 474 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 385 , 430 , 475 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 386 , 431 , 476 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 387 , 432 , 477 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 935 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , 893 , 844 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , -1 , 935 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 751 , 796 , 841 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 889 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 937 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 752 , 797 , 842 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 894 , 939 , 753 , 798 , 843 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , 893 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 937 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 845 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 806 , -1 , -1 , -1 , -1 , 846 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 806 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 936 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , 806 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 936 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 936 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 763 , 808 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 764 , 809 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , 851 , -1 , 852 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],];\n\nexport const objmap = [\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],];\nexport const animatedsprites = [\n{ x: 736, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 256, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 224, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 224, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 192, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 192, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 160, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 128, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 96, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 64, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 736, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 512, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 512, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 544, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 576, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 736, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 768, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 800, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 832, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 1440, y: 352, w: 32, h: 32, layer: 1, sheet: \"campfire.json\", animation: \"pixels_large\" },\n{ x: 1664, y: 576, w: 208, h: 208, layer: 1, sheet: \"windmill.json\", animation: \"pixels_large\" },\n{ x: 1440, y: 768, w: 208, h: 208, layer: 1, sheet: \"windmill.json\", animation: \"pixels_large\" },\n{ x: 1120, y: 608, w: 208, h: 208, layer: 1, sheet: \"windmill.json\", animation: \"pixels_large\" },\n{ x: 768, y: 736, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 768, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 800, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 832, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 864, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 864, y: 1024, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1056, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 864, y: 1088, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1088, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1120, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1152, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1184, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 928, y: 1152, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 736, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 768, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 800, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 832, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n];\n"
  },
  {
    "path": "src/editor/maps/gentle.js",
    "content": "// Map generated by assettool.js [Wed Oct 18 2023 21:07:27 GMT-0700 (Pacific Daylight Time)]\n\nexport const tilesetpath = \"./tilesets/gentle-obj.png\"\nexport const tiledim = 32\nexport const screenxtiles = 45\nexport const screenytiles = 32\nexport const tilesetpxw = 1440\nexport const tilesetpxh = 1024\n\nexport const bgtiles = [\n   [\n[ 732 , 777 , 822 , 867 , 912 , 957 , 912 , 957 , 1002 , 1047 , 912 , 957 , 1002 , 1047 , 912 , 957 , 1002 , 1047 , 1001 , 1046 , 946 , 991 , 1035 , 731 , 776 , 821 , 866 , 911 , 956 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 594 , 639 , 684 , 271 , ],\n[ 733 , 778 , 823 , 868 , 913 , 958 , 913 , 958 , 1003 , 1048 , 913 , 958 , 1003 , 1048 , 913 , 958 , 1003 , 1048 , 856 , 901 , 946 , 991 , 1036 , 732 , 777 , 822 , 867 , 912 , 957 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 595 , 640 , 685 , 271 , ],\n[ 734 , 779 , 824 , 869 , 914 , 959 , 914 , 959 , 1004 , 1049 , 914 , 959 , 1004 , 1049 , 914 , 959 , 1004 , 1049 , 857 , 902 , 947 , 992 , 1037 , 733 , 778 , 823 , 868 , 913 , 958 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 594 , 639 , 684 , 271 , ],\n[ 735 , 780 , 825 , 870 , 915 , 960 , 915 , 960 , 1005 , 1050 , 915 , 960 , 1005 , 1050 , 915 , 960 , 1087 , 1132 , 858 , 903 , 948 , 993 , 1038 , 734 , 779 , 824 , 869 , 914 , 959 , 271 , 271 , 180 , 225 , 225 , 315 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 595 , 640 , 685 , 271 , ],\n[ 736 , 781 , 826 , 871 , 916 , 278 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 859 , 904 , 949 , 994 , 1039 , 735 , 780 , 825 , 870 , 915 , 960 , 271 , 271 , 181 , 226 , 226 , 140 , 270 , 315 , 271 , 271 , 271 , 271 , 271 , 271 , 541 , 586 , 631 , 686 , 271 , ],\n[ 737 , 782 , 827 , 872 , 917 , 233 , 271 , 271 , 271 , 271 , 271 , 233 , 271 , 271 , 271 , 271 , 1088 , 1133 , 860 , 905 , 950 , 995 , 1040 , 736 , 781 , 826 , 871 , 916 , 961 , 271 , 271 , 181 , 226 , 278 , 272 , 271 , 316 , 271 , 271 , 271 , 271 , 271 , 551 , 542 , 587 , 632 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 235 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 1089 , 1134 , 861 , 906 , 951 , 996 , 1041 , 737 , 782 , 827 , 872 , 917 , 962 , 271 , 271 , 181 , 226 , 226 , 272 , 271 , 675 , 271 , 271 , 271 , 271 , 272 , 272 , 642 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 235 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 1090 , 1135 , 862 , 907 , 952 , 997 , 1042 , 738 , 783 , 828 , 873 , 918 , 963 , 271 , 271 , 183 , 228 , 228 , 184 , 271 , 675 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 182 , 280 , 317 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 962 , 0 , 135 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 140 , 273 , 318 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 0 , 45 , 90 , 135 , 271 , 271 , 271 , 271 , 962 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 962 , 3 , 48 , 93 , 271 , 271 , 271 , 271 , 271 , 962 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 271 , 271 , 962 , 962 , 962 , 962 , 962 , 279 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 271 , 962 , 962 , 962 , 962 , 143 , 188 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 278 , 962 , 962 , 962 , 144 , 189 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 271 , 271 , 271 , 271 , 145 , 190 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1 , 136 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 3 , 138 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 360 , 405 , 405 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 1270 , 1315 , 1360 , 1405 , 226 , 360 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 409 , 451 , 451 , 451 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 1271 , 1316 , 226 , 226 , 226 , 361 , 409 , 409 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 407 , 452 , 452 , 452 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 450 , 362 , 406 , 407 , 451 , 451 , 271 , 271 , 271 , 405 , 405 , 450 , 320 , 409 , 451 , 452 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 405 , 450 , 405 , 450 , 450 , 320 , 406 , 406 , 451 , 589 , 451 , 451 , 451 , 406 , 451 , 409 , 451 , 409 , 451 , 452 , 406 , 275 , 405 , 450 , 405 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 406 , 406 , 406 , 451 , 451 , 407 , 406 , 407 , 452 , 451 , 451 , 451 , 451 , 407 , 589 , 452 , 452 , 451 , 406 , 451 , 406 , 406 , 407 , 451 , 451 , 275 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 408 , 453 , 408 , 453 , 319 , 406 , 407 , 406 , 451 , 451 , 451 , 589 , 451 , 634 , 452 , 589 , 406 , 451 , 634 , 452 , 274 , 408 , 408 , 319 , 451 , 451 , 451 , 451 , 275 , 405 , 405 , 450 , 405 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 361 , 634 , 452 , 407 , 634 , 451 , 451 , 451 , 451 , 407 , 452 , 452 , 407 , 452 , 452 , 274 , 498 , 271 , 271 , 363 , 408 , 453 , 408 , 453 , 408 , 453 , 319 , 451 , 451 , 275 , 405 , 450 , 405 , 450 , 405 , 450 , 495 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 362 , 409 , 451 , 451 , 451 , 451 , 271 , 271 , 271 , 408 , 408 , 319 , 407 , 452 , 409 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 406 , 451 , 406 , 451 , 451 , 451 , 451 , 544 , 451 , 496 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 362 , 407 , 409 , 451 , 451 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 409 , 409 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 544 , 407 , 544 , 589 , 452 , 452 , 634 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 363 , 408 , 453 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 408 , 453 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 319 , 407 , 452 , 452 , 499 , 544 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 319 , 544 , 499 , 499 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 407 , 499 , 499 , 499 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 271 , 271 , 271 , 272 , 271 , 278 , 271 , 271 , 271 , 279 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 544 , 499 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 1129 , 1174 , 1219 , 1264 , 900 , 945 , 990 , 1035 , 1174 , 1219 , 1264 , 1309 , 1354 , 280 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 407 , 499 , 499 , 499 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 721 , 766 , 811 , 856 , 901 , 946 , 991 , 1036 , 1175 , 1220 , 1265 , 1310 , 1355 , 1400 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 499 , 499 , 407 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 722 , 767 , 812 , 857 , 902 , 947 , 992 , 1037 , 1176 , 1221 , 1266 , 1311 , 1356 , 1401 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 361 , 544 , 634 , 452 , 544 , 452 , 497 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 723 , 768 , 813 , 858 , 903 , 948 , 993 , 1038 , 1177 , 1222 , 1267 , 1312 , 1357 , 1402 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 362 , 407 , 452 , 452 , 452 , 274 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 724 , 769 , 814 , 859 , 904 , 949 , 994 , 1039 , 1178 , 1223 , 1268 , 1313 , 1358 , 1403 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 363 , 408 , 453 , 453 , 453 , 498 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 725 , 770 , 815 , 860 , 905 , 950 , 995 , 1040 , 1179 , 1224 , 1269 , 1314 , 1359 , 1404 , 271 , 271 , 6 , 51 , 96 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 726 , 771 , 816 , 861 , 906 , 951 , 996 , 1041 , 1180 , 1225 , 1270 , 1315 , 1360 , 1405 , 271 , 271 , 7 , 52 , 97 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 735 , 780 , 825 , 870 , 907 , 952 , 997 , 1042 , 1181 , 1226 , 1271 , 234 , 1361 , 280 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 962 , 962 , 962 , 962 , 962 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 227 , 1129 , 1174 , 1219 , 227 , 1309 , 227 , 278 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 962 , 962 , 962 , 233 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 234 , 1130 , 1175 , 1220 , 1265 , 1310 , 1355 , 1400 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 227 , 1131 , 1176 , 1221 , 1266 , 1311 , 1356 , 1401 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 1177 , 1222 , 1267 , 1312 , 1357 , 1402 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 728 , 773 , 818 , 863 , 908 , 953 , 998 , 1043 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 729 , 774 , 819 , 864 , 909 , 954 , 999 , 1044 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 234 , 730 , 775 , 820 , 865 , 910 , 955 , 1000 , 1045 , 271 , 271 , 271 , 280 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 1007 , 143 , 188 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 731 , 776 , 821 , 866 , 911 , 956 , 1001 , 1046 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 736 , 781 , 826 , 871 , 962 , 962 , 962 , 962 , 1007 , 1007 , 1007 , 1007 , 144 , 189 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1088 , 1133 , 1178 , 1223 , 1268 , 1313 , 1358 , 1403 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 737 , 782 , 827 , 872 , 271 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 1007 , 145 , 190 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1089 , 1134 , 1179 , 1224 , 1269 , 1314 , 1359 , 1404 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 738 , 783 , 828 , 873 , 271 , 271 , 271 , 271 , 271 , 271 , 233 , 271 , 1007 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 280 , 1135 , 1180 , 1225 , 1270 , 1315 , 1360 , 1405 , 271 , 271 , 271 , 271 , 271 , 271 , ],\n[ 739 , 784 , 829 , 874 , 271 , 271 , 271 , 1010 , 1055 , 962 , 962 , 271 , 271 , 271 , 1309 , 1354 , 1399 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 280 , 143 , 188 , 1226 , 1271 , 227 , 1361 , 234 , 271 , 280 , 1129 , 1174 , 1219 , 1264 , ],\n[ 740 , 785 , 830 , 875 , 920 , 965 , 1010 , 740 , 785 , 830 , 875 , 920 , 965 , 1010 , 1310 , 1355 , 1400 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 144 , 189 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1130 , 1175 , 1220 , 1265 , ],\n[ 741 , 786 , 831 , 876 , 921 , 966 , 1011 , 741 , 786 , 831 , 876 , 921 , 966 , 1011 , 1311 , 1356 , 1401 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 145 , 190 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1131 , 1176 , 1221 , 1266 , ],\n[ 742 , 787 , 832 , 877 , 922 , 967 , 1012 , 742 , 787 , 832 , 877 , 922 , 967 , 1012 , 1312 , 1357 , 1402 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 1177 , 1222 , 1267 , ],\n[ 743 , 788 , 833 , 878 , 923 , 968 , 1013 , 743 , 788 , 833 , 878 , 923 , 968 , 1013 , 1313 , 1358 , 1403 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 278 , 271 , 271 , 271 , 271 , 1088 , 1133 , 1178 , 1223 , 1268 , ],\n[ 1178 , 1223 , 1268 , 1223 , 1268 , 1223 , 1268 , 1223 , 1268 , 1223 , 1268 , 1178 , 1223 , 1268 , 1314 , 1359 , 1404 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 227 , 271 , 1134 , 1179 , 1224 , 1269 , ],\n[ 1179 , 1224 , 1269 , 1224 , 1269 , 1224 , 1269 , 1224 , 1269 , 1224 , 1269 , 1179 , 1224 , 1269 , 1315 , 1360 , 1405 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 233 , 271 , 271 , 1143 , 1188 , 1233 , ],\n[ 1180 , 1225 , 1270 , 1225 , 1270 , 1225 , 1270 , 1225 , 1270 , 1225 , 1270 , 1180 , 1225 , 1270 , 1316 , 1361 , 1406 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1009 , 1054 , 1099 , 1144 , 1189 , 1234 , ],\n[ 1181 , 1226 , 1271 , 1226 , 1271 , 1226 , 1271 , 1226 , 1271 , 1226 , 1271 , 1181 , 1226 , 1271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 272 , 1129 , 272 , 965 , 1010 , 1055 , 920 , 965 , 1010 , 1055 , 1100 , 1145 , 1190 , 1235 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 272 , 1130 , 1219 , 966 , 1011 , 1056 , 921 , 966 , 1011 , 1056 , 1101 , 1146 , 1191 , 1236 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1086 , 1131 , 922 , 967 , 1012 , 1057 , 922 , 967 , 1012 , 1057 , 1102 , 1147 , 1192 , 1237 , ],\n[ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 1087 , 1132 , 923 , 968 , 1013 , 1058 , 923 , 968 , 1013 , 1058 , 1103 , 1148 , 1193 , 1238 , ],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 231 , 276 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 232 , 277 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 416 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 370 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 371 , 416 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 763 , 808 , 754 , 799 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 8 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 417 , 462 , 507 , 552 , 597 , 642 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 764 , 809 , 755 , 800 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 9 , 54 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 844 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 762 , 751 , 796 , 841 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 752 , 797 , 842 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 141 , 186 , -1 , -1 , -1 , -1 , -1 , -1 , 896 , -1 , -1 , 547 , 592 , 637 , 682 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 753 , 798 , 843 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 142 , 187 , -1 , -1 , 11 , -1 , -1 , -1 , 191 , 236 , -1 , 548 , 593 , 638 , 683 , 682 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 11 , 56 , 101 , 146 , 191 , 236 , -1 , 548 , 593 , 638 , 683 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 278 , -1 , -1 , -1 , -1 , -1 , -1 , 12 , 57 , 102 , 147 , 192 , 237 , 941 , -1 , 594 , 639 , 684 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 13 , 58 , 103 , 148 , 193 , 238 , -1 , -1 , 595 , 640 , 685 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1026 , 1071 , 1116 , 1161 , -1 , -1 , -1 , 278 , -1 , -1 , -1 , 15 , 14 , 59 , 104 , 149 , 194 , 239 , 850 , -1 , 594 , 639 , 684 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1027 , 1072 , 1117 , 1162 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 15 , 60 , 105 , 150 , 195 , 850 , -1 , -1 , 595 , 640 , 685 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1028 , 1073 , 1118 , 1163 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 850 , -1 , 551 , 596 , 641 , 686 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 1029 , 1074 , 1119 , 1164 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 371 , 416 , -1 , -1 , -1 , -1 , 551 , 596 , 641 , 686 , 687 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 417 , 507 , 462 , 507 , 552 , 597 , 642 , 642 , 687 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 547 , 592 , 637 , 682 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 231 , 276 , -1 , -1 , -1 , 894 , 939 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 548 , 593 , 638 , 683 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 232 , 277 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 549 , 594 , 639 , 684 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 380 , 380 , 425 , 470 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 381 , 381 , 426 , 471 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 382 , 382 , 427 , 472 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 280 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 383 , 428 , 473 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 550 , 595 , 640 , 685 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 384 , 429 , 474 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 551 , 596 , 641 , 686 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 233 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 385 , 385 , 430 , 475 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 462 , 507 , 552 , 552 , 597 , 642 , 687 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 386 , 386 , 431 , 476 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 387 , 387 , 432 , 477 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 11 , 56 , 101 , 146 , 191 , 236 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 279 , -1 , -1 , -1 , -1 , 12 , 57 , 102 , 147 , 192 , 237 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 380 , 425 , 470 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 13 , 58 , 103 , 148 , 193 , 238 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 381 , 426 , 471 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 14 , 59 , 104 , 149 , 194 , 239 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 382 , 427 , 472 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 15 , 60 , 105 , 150 , 195 , 240 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 383 , 428 , 473 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 384 , 429 , 474 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 385 , 430 , 475 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 386 , 431 , 476 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 387 , 432 , 477 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 935 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , 893 , 844 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , -1 , 935 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 751 , 796 , 841 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 889 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 937 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 752 , 797 , 842 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 894 , 939 , 753 , 798 , 843 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 893 , 893 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 937 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 845 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 806 , -1 , -1 , -1 , -1 , 846 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 806 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 936 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , 806 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 936 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 936 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 763 , 808 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 764 , 809 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 934 , -1 , -1 , -1 , 851 , -1 , 852 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],];\n\nexport const objmap = [\n[\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , 367 , 367 , 367 , 367 , -1 , 458 , 458 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , 458 , 458 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , 367 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 458 , 458 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 458 , 458 , 458 , 458 , 458 , 458 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , 367 , 367 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , -1 , 367 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , 367 , ],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],];\nexport const animatedsprites = [\n{ x: 1440, y: 352, w: 32, h: 32, layer: 1, sheet: \"campfire.json\", animation: \"pixels_large\" },\n{ x: 736, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 256, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 224, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 224, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 192, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 192, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 160, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 128, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 96, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 64, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 736, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 448, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 832, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 512, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 512, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 544, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 576, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 768, y: 736, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 768, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 800, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 832, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 800, y: 864, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 864, y: 1024, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1056, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 864, y: 1088, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1088, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1120, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1152, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 896, y: 1184, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 928, y: 1152, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 736, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 768, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 800, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 832, y: 320, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 1664, y: 576, w: 208, h: 208, layer: 1, sheet: \"windmill.json\", animation: \"pixels_large\" },\n{ x: 1440, y: 768, w: 208, h: 208, layer: 1, sheet: \"windmill.json\", animation: \"pixels_large\" },\n{ x: 1120, y: 608, w: 208, h: 208, layer: 1, sheet: \"windmill.json\", animation: \"pixels_large\" },\n{ x: 736, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 768, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 800, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 832, y: 384, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n];\n"
  },
  {
    "path": "src/editor/maps/gentleanim.js",
    "content": "// Map generated by assettool.js [Sat Sep 30 2023 23:42:21 GMT-0700 (Pacific Daylight Time)]\n\nexport const tilesetpath = \"./tilesets/gentle.png\"\nexport const tiledim = 32\nexport const screenxtiles = 40\nexport const screenytiles = 32\nexport const tilesetpxw = 1280\nexport const tilesetpxh = 1024\n\nexport const bgtiles = [\n   [\n[ 652 , 692 , 732 , 772 , 812 , 852 , 892 , 812 , 852 , 892 , 932 , 812 , 852 , 892 , 932 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 0 , 120 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 653 , 693 , 733 , 773 , 813 , 853 , 1089 , 813 , 853 , 893 , 933 , 813 , 853 , 893 , 933 , 857 , 242 , 242 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 654 , 694 , 734 , 774 , 1010 , 1050 , 1090 , 814 , 854 , 894 , 934 , 814 , 854 , 894 , 934 , 857 , 242 , 242 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 655 , 695 , 735 , 775 , 815 , 970 , 1091 , 815 , 855 , 895 , 935 , 815 , 855 , 895 , 935 , 857 , 242 , 242 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 656 , 696 , 736 , 776 , 242 , 242 , 242 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 248 , 241 , 241 , 241 , 160 , 200 , 200 , 200 , 200 , 280 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 657 , 697 , 737 , 777 , 242 , 242 , 242 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 161 , 201 , 201 , 201 , 250 , 281 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 658 , 698 , 738 , 778 , 242 , 242 , 242 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 161 , 201 , 201 , 201 , 249 , 281 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 655 , 695 , 735 , 777 , 241 , 241 , 241 , 241 , 0 , 40 , 80 , 120 , 241 , 241 , 241 , 241 , 241 , 241 , 161 , 201 , 250 , 201 , 201 , 600 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 656 , 696 , 736 , 776 , 241 , 241 , 241 , 241 , 1 , 41 , 81 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 161 , 201 , 201 , 250 , 201 , 601 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 657 , 697 , 737 , 777 , 241 , 241 , 241 , 210 , 3 , 43 , 83 , 123 , 241 , 241 , 241 , 241 , 241 , 241 , 161 , 201 , 249 , 201 , 201 , 281 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 658 , 698 , 738 , 778 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 210 , 241 , 241 , 161 , 201 , 201 , 201 , 201 , 281 , 241 , 241 , 241 , 2 , 122 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 659 , 699 , 739 , 779 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 163 , 203 , 203 , 203 , 203 , 283 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 658 , 698 , 738 , 778 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 122 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 659 , 699 , 739 , 779 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 2 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 360 , 400 , 400 , 400 , 360 , 400 , 400 , 400 , 440 , 50 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 122 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 362 , 402 , 362 , 402 , 402 , 401 , 402 , 402 , 441 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 363 , 403 , 363 , 403 , 403 , 363 , 403 , 284 , 245 , 400 , 413 , 493 , 533 , 573 , 613 , 360 , 400 , 440 , 241 , 241 , 320 , 360 , 400 , 400 , 440 , 241 , 241 , 2 , 122 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 964 , 1004 , 1044 , 1084 , 1124 , 1164 , 1204 , 322 , 362 , 362 , 364 , 362 , 362 , 362 , 362 , 564 , 401 , 441 , 241 , 242 , 322 , 404 , 401 , 362 , 441 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 965 , 1005 , 1045 , 1085 , 1125 , 1165 , 1205 , 323 , 284 , 362 , 404 , 404 , 444 , 444 , 444 , 362 , 524 , 245 , 360 , 360 , 285 , 444 , 444 , 362 , 442 , 241 , 241 , 2 , 122 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 966 , 1006 , 1046 , 1086 , 1126 , 1166 , 1206 , 1246 , 321 , 362 , 444 , 484 , 362 , 362 , 362 , 362 , 401 , 564 , 361 , 361 , 361 , 444 , 444 , 484 , 441 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 967 , 1007 , 1047 , 1087 , 1127 , 1167 , 1207 , 1247 , 322 , 402 , 364 , 455 , 495 , 535 , 575 , 615 , 363 , 403 , 363 , 284 , 361 , 444 , 444 , 484 , 442 , 241 , 241 , 1 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 210 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 968 , 1008 , 1048 , 1088 , 1128 , 1168 , 1208 , 1248 , 323 , 363 , 403 , 443 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 322 , 362 , 362 , 484 , 484 , 442 , 241 , 241 , 2 , 122 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 969 , 1009 , 1049 , 1089 , 1129 , 1169 , 1209 , 1249 , 857 , 857 , 857 , 857 , 857 , 857 , 210 , 241 , 241 , 241 , 241 , 323 , 363 , 284 , 362 , 362 , 442 , 241 , 241 , 2 , 122 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 970 , 1010 , 1050 , 1090 , 1130 , 1170 , 1210 , 1250 , 857 , 857 , 857 , 857 , 857 , 857 , 241 , 241 , 128 , 168 , 241 , 241 , 241 , 323 , 403 , 403 , 443 , 241 , 241 , 84 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 971 , 1011 , 857 , 857 , 857 , 1044 , 1084 , 1124 , 1164 , 1044 , 1084 , 1204 , 1244 , 857 , 857 , 241 , 129 , 169 , 241 , 241 , 241 , 241 , 242 , 241 , 241 , 0 , 45 , 4 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 857 , 857 , 857 , 857 , 857 , 1005 , 1045 , 1085 , 1125 , 1045 , 1085 , 1205 , 1245 , 857 , 248 , 241 , 130 , 170 , 241 , 241 , 241 , 241 , 0 , 80 , 120 , 1 , 83 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 857 , 857 , 857 , 857 , 857 , 648 , 688 , 728 , 768 , 808 , 848 , 888 , 928 , 857 , 857 , 249 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 41 , 5 , 84 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 857 , 857 , 857 , 857 , 857 , 649 , 689 , 729 , 769 , 809 , 849 , 889 , 929 , 857 , 857 , 857 , 241 , 241 , 241 , 241 , 241 , 241 , 2 , 42 , 122 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 857 , 857 , 857 , 857 , 1004 , 1044 , 1084 , 730 , 770 , 810 , 850 , 890 , 930 , 857 , 857 , 857 , 241 , 241 , 241 , 241 , 241 , 241 , 3 , 43 , 123 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 857 , 857 , 857 , 965 , 1005 , 1045 , 1085 , 731 , 771 , 811 , 851 , 891 , 1205 , 1245 , 857 , 857 , 249 , 241 , 241 , 241 , 241 , 241 , 208 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 857 , 857 , 857 , 966 , 1006 , 1046 , 1086 , 732 , 772 , 812 , 852 , 892 , 1206 , 1246 , 857 , 857 , 857 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 857 , 857 , 857 , 967 , 1007 , 1047 , 1087 , 733 , 773 , 813 , 853 , 893 , 1207 , 1247 , 857 , 857 , 857 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 857 , 857 , 857 , 968 , 1008 , 1048 , 1088 , 734 , 1048 , 1088 , 1128 , 1168 , 1208 , 1248 , 857 , 857 , 857 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 857 , 857 , 857 , 969 , 1009 , 1049 , 1089 , 727 , 1049 , 1089 , 1169 , 1169 , 1209 , 1249 , 857 , 857 , 857 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 857 , 249 , 241 , 242 , 1010 , 1050 , 1090 , 1130 , 1130 , 1170 , 1170 , 1170 , 1210 , 1250 , 857 , 857 , 250 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 242 , 242 , 249 , 1091 , 1131 , 1131 , 1171 , 1211 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 6 , 46 , 86 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 0 , 120 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 7 , 47 , 87 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 2 , 121 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 0 , 40 , 40 , 40 , 40 , 40 , 120 , 241 , 2 , 122 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 41 , 41 , 41 , 41 , 41 , 5 , 80 , 45 , 122 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 41 , 41 , 41 , 41 , 82 , 4 , 43 , 83 , 123 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 41 , 41 , 41 , 41 , 41 , 85 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 1 , 41 , 41 , 41 , 41 , 41 , 123 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 3 , 43 , 43 , 43 , 43 , 43 , 123 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 248 , 241 , 201 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 1004 , 1044 , 1084 , 1124 , 1164 , 1124 , 1164 , 1204 , 1244 , 201 , 201 , 201 , 970 , 210 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 652 , 692 , 732 , 772 , 812 , 1125 , 1165 , 1205 , 1245 , 201 , 201 , 201 , 201 , 970 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 653 , 693 , 733 , 773 , 1089 , 1129 , 1169 , 1209 , 1249 , 201 , 201 , 201 , 128 , 168 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 654 , 694 , 734 , 774 , 1090 , 1130 , 1170 , 1210 , 1250 , 899 , 201 , 250 , 129 , 169 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 655 , 695 , 735 , 775 , 815 , 855 , 970 , 899 , 899 , 201 , 201 , 970 , 130 , 170 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 656 , 696 , 736 , 776 , 208 , 856 , 970 , 936 , 249 , 250 , 970 , 970 , 970 , 249 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 657 , 697 , 737 , 777 , 817 , 857 , 970 , 208 , 201 , 1004 , 1044 , 1084 , 1204 , 1244 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 658 , 698 , 738 , 778 , 818 , 858 , 970 , 938 , 978 , 1005 , 1045 , 1165 , 1205 , 1245 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 659 , 699 , 739 , 779 , 208 , 859 , 208 , 939 , 979 , 1006 , 1046 , 1166 , 1206 , 1246 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 660 , 700 , 740 , 780 , 820 , 860 , 900 , 940 , 980 , 1020 , 1060 , 1100 , 1207 , 1247 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 661 , 701 , 741 , 781 , 821 , 861 , 901 , 941 , 981 , 1021 , 1061 , 1101 , 1208 , 1248 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 662 , 702 , 742 , 782 , 822 , 862 , 902 , 942 , 982 , 1022 , 1062 , 1102 , 1209 , 1249 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 663 , 703 , 743 , 783 , 823 , 863 , 903 , 943 , 983 , 1023 , 1063 , 1207 , 1247 , 1250 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 661 , 701 , 741 , 781 , 821 , 861 , 901 , 941 , 981 , 1021 , 1061 , 1208 , 1248 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 662 , 702 , 742 , 782 , 822 , 862 , 902 , 942 , 982 , 1022 , 1062 , 1209 , 1249 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n[ 663 , 703 , 743 , 783 , 823 , 863 , 903 , 943 , 983 , 1023 , 1063 , 1210 , 1250 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 543 , 583 , 623 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 368 , 529 , 569 , 609 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 544 , 584 , 857 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 367 , 368 , 202 , 530 , 570 , 610 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 545 , 585 , 857 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 328 , 368 , 202 , 208 , 530 , 570 , 610 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 546 , 586 , 857 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 329 , 202 , 250 , 202 , 481 , 521 , 561 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 329 , 248 , 202 , 491 , 482 , 522 , 562 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 126 , 166 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 372 , 452 , 492 , 492 , 532 , 572 , 612 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 127 , 167 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 340 , 380 , 420 , 460 , 500 , 540 , 580 , 620 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 341 , 381 , 421 , 461 , 501 , 541 , 581 , 621 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 342 , 382 , 422 , 462 , 502 , 760 , 582 , 248 , 880 , 540 , 580 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 210 , -1 , -1 , -1 , 206 , 246 , -1 , 343 , 383 , 423 , 463 , 503 , 761 , 801 , 841 , 881 , 541 , 581 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 207 , 247 , -1 , 344 , 384 , 424 , 464 , 504 , 762 , 802 , 842 , 882 , 542 , 582 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 242 , 242 , 242 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 345 , 385 , 425 , 465 , 505 , 763 , 803 , 843 , 883 , 543 , 583 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 367 , 407 , 447 , 487 , 527 , 567 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 342 , 382 , 422 , 462 , 502 , 764 , 804 , 844 , 884 , 544 , 584 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 488 , 528 , 568 , 608 , 567 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 210 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 343 , 383 , 423 , 463 , 503 , 765 , 805 , 845 , 885 , 545 , 585 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 488 , 528 , 568 , 608 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 344 , 384 , 424 , 464 , 504 , 766 , 806 , 846 , 886 , 546 , 586 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 345 , 385 , 425 , 465 , 505 , 767 , 585 , 847 , 887 , 547 , 587 , -1 , -1 , -1 , -1 , ],\n[ 17 , 57 , 97 , 137 , 177 , 217 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 346 , 386 , 426 , 466 , 506 , 546 , 586 , 626 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 18 , 58 , 98 , 138 , 178 , 218 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 347 , 387 , 427 , 467 , 507 , 547 , 587 , 627 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 19 , 59 , 99 , 139 , 179 , 219 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 208 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 20 , 60 , 100 , 140 , 180 , 220 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 209 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 21 , 61 , 101 , 141 , 181 , 221 , -1 , -1 , -1 , -1 , -1 , -1 , 491 , 531 , 571 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 22 , 62 , 102 , 142 , 182 , 222 , -1 , -1 , -1 , -1 , -1 , -1 , 492 , 532 , 572 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 210 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 23 , 63 , 103 , 143 , 183 , 223 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 128 , 168 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 17 , 57 , 97 , 137 , 177 , 217 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 210 , -1 , -1 , -1 , -1 , 129 , 169 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 18 , 58 , 98 , 138 , 178 , 218 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 130 , 170 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 19 , 59 , 99 , 139 , 179 , 219 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 20 , 60 , 100 , 140 , 180 , 220 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 21 , 61 , 101 , 141 , 181 , 221 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 11 , 51 , 91 , 131 , 171 , 211 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 22 , 62 , 102 , 142 , 182 , 222 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 12 , 52 , 92 , 132 , 172 , 212 , 248 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 23 , 63 , 103 , 143 , 183 , 223 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 13 , 53 , 93 , 133 , 173 , 213 , 249 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 386 , 426 , 466 , 506 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 14 , 54 , 94 , 134 , 174 , 214 , 250 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 387 , 427 , 467 , 507 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 340 , 380 , 420 , 460 , 500 , 540 , 580 , 620 , -1 , 15 , 55 , 95 , 135 , 175 , 215 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 341 , 381 , 421 , 461 , 501 , 541 , 581 , 621 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 342 , 382 , 422 , 462 , 502 , 542 , 582 , 622 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 343 , 383 , 423 , 463 , 503 , 543 , 583 , 623 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 8 , 48 , -1 , -1 , -1 , -1 , 344 , 384 , 424 , 464 , 504 , 544 , 584 , 624 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 126 , 166 , -1 , -1 , -1 , 9 , 49 , -1 , -1 , -1 , -1 , 345 , 385 , 425 , 465 , 505 , 545 , 585 , 625 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 127 , 167 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 346 , 386 , 426 , 466 , 506 , 546 , 586 , 626 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 347 , 387 , 427 , 467 , 507 , 547 , 587 , 627 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , 11 , 51 , 91 , 131 , 171 , 211 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , 12 , 52 , 92 , 132 , 172 , 212 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , 13 , 53 , 93 , 133 , 173 , 213 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , 14 , 54 , 94 , 134 , 174 , 214 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , 15 , 55 , 95 , 135 , 175 , 215 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 8 , 48 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , 8 , 48 , -1 , -1 , -1 , -1 , -1 , 9 , 49 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , 9 , 49 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 206 , 246 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 207 , 247 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],];\n\nexport const objmap = [\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],];\nexport const animatedsprites = [\n{ x: 576, y: 320, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 608, y: 320, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 544, y: 288, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 608, y: 672, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 576, y: 704, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 608, y: 704, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 576, y: 672, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 480, y: 224, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 480, y: 160, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 480, y: 96, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 608, y: 352, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 544, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 576, y: 480, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 576, y: 512, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 608, y: 512, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 608, y: 544, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 640, y: 704, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 672, y: 672, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 640, y: 640, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 640, y: 672, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 640, y: 736, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 704, y: 704, w: 32, h: 32, layer: 1, sheet: \"gentlesparkle.json\", animation: \"pixels_large\" },\n{ x: 544, y: 352, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 576, y: 352, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 608, y: 384, w: 32, h: 96, layer: 1, sheet: \"gentlewaterfall.json\", animation: \"pixels_large\" },\n{ x: 1248, y: 256, w: 208, h: 208, layer: 1, sheet: \"windmill.json\", animation: \"pixels_large\" },\n{ x: 1728, y: 160, w: 32, h: 32, layer: 1, sheet: \"campfire.json\", animation: \"pixels_large\" },\n{ x: 544, y: 416, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 576, y: 416, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n{ x: 608, y: 448, w: 32, h: 64, layer: 2, sheet: \"gentlesplash.json\", animation: \"pixels_large\" },\n];\n"
  },
  {
    "path": "src/editor/maps/mage3.js",
    "content": "// Map generated by assettool.jsThu Aug 31 2023 00:08:34 GMT-0700 (Pacific Daylight Time)\n\nexport const tilesetpath = \"./tilesets/magecity.png\"\nexport const tiledim = 32\nexport const screenxtiles = 8\nexport const screenytiles = 44\nexport const tilesetpxw = 256\nexport const tilesetpxh = 1408\n\nexport const bgtiles = [\n   [\n[ 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194 , 194],\n[ 193 , 201 , 35 , 43 , 33 , 33 , 33 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 0 , 1 , 1 , 1 , 35 , 32 , 40 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 211 , 0 , 211 , 211 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 33 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 1 , 1 , 1 , 1 , 36 , 43 , 41 , 264 , 272 , 0 , 211 , 238 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 236 , 0 , 0 , 1 , 1 , 1 , 1 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 33 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 1 , 1 , 1 , 1 , 36 , 33 , 41 , 265 , 273 , 238 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 1 , 1 , 1 , 1 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 33 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 1 , 1 , 1 , 1 , 36 , 326 , 334 , 235 , 238 , 1 , 1 , 1 , 1 , 1 , 1 , 72 , 88 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 33 , 1 , 1 , 1 , 1 , 236 , 211 , 211 , 211 , 211 , 211 , 238 , 1 , 1 , 227 , 0 , 0 , 1 , 1 , 1 , 37 , 34 , 42 , 235 , 1 , 1 , 1 , 1 , 1 , 1 , 72 , 75 , 83 , 88 , 1 , 1 , 1 , 236 , 0 , 1 , 1 , 1 , 1 , 1],\n[ 193 , 201 , 36 , 43 , 33 , 33 , 33 , 1 , 1 , 1 , 1 , 1 , 9 , 1 , 1 , 1 , 1 , 1 , 9 , 1 , 236 , 211 , 211 , 211 , 211 , 211 , 211 , 211 , 211 , 238 , 1 , 1 , 1 , 1 , 1 , 72 , 75 , 81 , 81 , 83 , 88 , 1 , 1 , 1 , 236 , 1 , 1 , 1 , 1 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 33 , 1 , 1 , 1 , 1 , 237 , 237 , 219 , 219 , 328 , 344 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 74 , 76 , 81 , 81 , 84 , 90 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 211 , 238 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 239 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 74 , 76 , 84 , 90 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 0 , 0 , 0 , 0 , 0 , 0 , 235 , 1 , 346 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 219 , 219 , 239 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 74 , 90 , 1 , 1 , 1 , 1 , 1 , 237 , 0 , 0 , 0 , 0 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 0 , 0 , 0 , 292 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 219 , 239 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 0 , 0 , 0 , 0 , 0 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 0 , 292 , 0 , 0 , 0 , 211 , 238 , 1 , 9 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 212 , 220 , 228 , 211 , 239 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 0 , 0 , 0 , 0 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 0 , 0 , 0 , 0 , 238 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 212 , 220 , 228 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 0 , 0 , 0 , 0 , 0 , 0 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 0 , 0 , 0 , 292 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 212 , 220 , 220 , 228 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1],\n[ 193 , 201 , 36 , 33 , 33 , 33 , 0 , 0 , 0 , 0 , 1 , 9 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 0 , 1 , 1 , 211 , 211 , 211 , 211 , 211 , 211 , 211 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 213 , 221 , 221 , 229 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1],\n[ 185 , 195 , 200 , 35 , 32 , 32 , 40 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 236 , 227 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 41 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 214 , 222 , 222 , 230 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1],\n[ 193 , 193 , 195 , 200 , 35 , 32 , 32 , 40 , 0 , 0 , 1 , 1 , 9 , 1 , 346 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 41 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1],\n[ 40 , 193 , 193 , 201 , 36 , 33 , 33 , 41 , 0 , 235 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 41 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1],\n[ 42 , 193 , 193 , 201 , 45 , 43 , 33 , 41 , 0 , 0 , 1 , 1 , 72 , 80 , 80 , 80 , 88 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 41 , 0 , 0 , 0 , 0 , 0 , 0 , 238 , 1 , 212 , 220 , 220 , 228 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1],\n[ 185 , 193 , 196 , 202 , 37 , 34 , 34 , 42 , 0 , 238 , 1 , 1 , 73 , 81 , 81 , 81 , 89 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 41 , 0 , 238 , 1 , 1 , 1 , 1 , 1 , 1 , 213 , 221 , 221 , 229 , 266 , 274 , 282 , 0 , 0 , 0 , 1 , 1 , 1],\n[ 185 , 196 , 202 , 34 , 34 , 34 , 42 , 1 , 1 , 1 , 1 , 72 , 75 , 81 , 81 , 81 , 83 , 88 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 214 , 222 , 222 , 230 , 267 , 275 , 283 , 0 , 0 , 0 , 1 , 1 , 1],\n[ 185 , 201 , 34 , 34 , 34 , 42 , 1 , 1 , 1 , 1 , 1 , 74 , 76 , 81 , 81 , 81 , 84 , 90 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 236 , 268 , 276 , 284 , 0 , 0 , 0 , 1 , 1 , 1],\n[ 193 , 201 , 36 , 326 , 334 , 1 , 1 , 237 , 219 , 239 , 1 , 1 , 73 , 81 , 81 , 81 , 89 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 236 , 277 , 285 , 0 , 0 , 0 , 1 , 1 , 1],\n[ 193 , 201 , 36 , 326 , 334 , 1 , 1 , 227 , 0 , 238 , 1 , 1 , 74 , 82 , 82 , 82 , 90 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 219 , 239 , 1 , 1 , 227 , 0 , 0 , 0 , 0 , 1 , 1 , 1],\n[ 193 , 201 , 35 , 32 , 40 , 40 , 1 , 236 , 238 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 219 , 52 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1],\n[ 193 , 195 , 200 , 35 , 32 , 40 , 1 , 1 , 1 , 1 , 9 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 346 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 52 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1],\n[ 193 , 193 , 195 , 200 , 32 , 32 , 32 , 40 , 239 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 52 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 236 , 0 , 238 , 1 , 1 , 227 , 0 , 0 , 0 , 0 , 1 , 1 , 1],\n[ 193 , 193 , 193 , 201 , 33 , 33 , 33 , 41 , 235 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 9 , 1 , 0 , 0 , 52 , 0 , 0 , 219 , 0 , 239 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 339 , 341 , 0 , 0 , 1 , 1 , 1],\n[ 193 , 193 , 193 , 201 , 38 , 46 , 33 , 41 , 0 , 239 , 1 , 1 , 1 , 1 , 1 , 346 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 52 , 0 , 0 , 0 , 0 , 235 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 337 , 337 , 0 , 1 , 1 , 1 , 1],\n[ 40 , 193 , 193 , 201 , 33 , 33 , 33 , 41 , 0 , 235 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 235 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 331 , 337 , 0 , 1 , 1 , 1 , 1],\n[ 42 , 193 , 193 , 201 , 33 , 33 , 33 , 41 , 0 , 238 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 219 , 239 , 1 , 1 , 1 , 1 , 1 , 237 , 0 , 339 , 337 , 0 , 1 , 1 , 1 , 1],\n[ 193 , 193 , 196 , 202 , 34 , 39 , 47 , 47 , 238 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 9 , 1 , 1 , 1 , 1 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 339 , 331 , 0 , 0 , 0 , 0 , 0],\n[ 196 , 194 , 202 , 34 , 34 , 48 , 56 , 238 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 292 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 339 , 331 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 35 , 32 , 40 , 0 , 49 , 57 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 211 , 285 , 293 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 339 , 341 , 339 , 0 , 0 , 0 , 339 , 331 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 0 , 49 , 57 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 285 , 293 , 0 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 331 , 340 , 340 , 0 , 0 , 0 , 340 , 340 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 0 , 49 , 57 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 292 , 227 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 340 , 331 , 340 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 0 , 49 , 57 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 293 , 0 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 340 , 340 , 331 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 43 , 41 , 0 , 49 , 57 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 0 , 219 , 219 , 219 , 0 , 0 , 0 , 0 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 340 , 340 , 340 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 48 , 57 , 57 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 264 , 272 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 38 , 46 , 41 , 49 , 57 , 57 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 265 , 273 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 49 , 57 , 57 , 239 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 266 , 274 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 49 , 57 , 57 , 0 , 239 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 227 , 0 , 0 , 0 , 0 , 0 , 0 , 267 , 275 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 49 , 57 , 57 , 0 , 235 , 239 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 237 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 268 , 276 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 49 , 57 , 57 , 0 , 0 , 0 , 219 , 219 , 219 , 0 , 0 , 219 , 219 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 277 , 0 , 0 , 52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 49 , 57 , 57 , 0 , 0 , 0 , 0 , 264 , 272 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , -1 , -1 , -1 , 35 , 32 , 32 , 40 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 49 , 57 , 57 , 0 , 0 , 0 , 0 , 265 , 273 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 190 , 198 , 206 , 1 , 36 , 33 , 33 , 41 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 49 , 57 , 57 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 36 , 33 , 33 , 41 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 49 , 57 , 57 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 36 , 33 , 33 , 41 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 201 , 36 , 33 , 41 , 49 , 57 , 57 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 36 , 33 , 33 , 41 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],\n[ 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 184 , 200 , 35 , 32 , 40],\n[ -1 , -1 , -1 , -1 , 189 , 197 , 205 , 212 , 220 , 228 , -1 , -1 , -1 , 190 , 198 , 206 , 212 , 220 , 228 , -1 , -1 , 193 , 193 , 193 , 193 , 201 , 168 , 176 , 19 , 27 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , 185 , 193 , 193 , 193 , 201 , 169 , 177 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , -1 , 190 , 188 , 206 , 213 , 221 , 229 , -1 , -1 , 185 , 193 , 193 , 193 , 201 , 232 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , 190 , 198 , 206 , 214 , 222 , 230 , -1 , -1 , -1 , 191 , 199 , 207 , 214 , 222 , 230 , -1 , -1 , 185 , 193 , 193 , 193 , 201 , 232 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , 190 , 198 , 203 , 205 , 212 , 220 , 228 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 186 , 194 , 194 , 194 , 202 , 232 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , 190 , 198 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 3 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 60 , 68 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 186 , 202 , 37 , 34 , 42],\n[ -1 , -1 , -1 , -1 , 191 , 199 , 199 , 207 , 214 , 222 , 230 , -1 , -1 , 105 , 113 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 61 , 69 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , 116 , 124 , 132 , -1 , 263 , 263 , 263 , -1 , 270 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 62 , 70 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , 117 , 125 , 133 , 130 , 138 , 146 , -1 , -1 , 327 , 263 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 96 , 104 , 112 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 184 , 200 , 35 , 32 , 40],\n[ -1 , -1 , 118 , 126 , 134 , 131 , 139 , 147 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 189 , 197 , 197 , 205 , 212 , 220 , 228 , 208 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , 119 , 127 , 135 , -1 , -1 , 233 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 198 , 206 , 209 , 209 , 209 , 209 , -1 , -1 , -1 , 19 , 27 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 318 , 263 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 198 , 206 , 209 , 209 , 209 , 209 , 189 , 197 , 205 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 319 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 191 , 199 , 199 , 207 , 212 , 220 , 228 , 208 , 190 , 198 , 206 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 316 , 316 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 263 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 189 , 197 , 205 , 212 , 220 , 228 , -1 , -1 , -1 , 191 , 199 , 207 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 148 , 153 , 161 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 148 , 148 , 148 , 19 , 27 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 96 , 104 , 112 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , 153 , 161 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 326 , 334 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 21 , 29 , -1 , -1 , 102 , 102 , 102 , 102 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , -1 , -1 , -1 , -1 , 189 , 197 , 197 , 197 , 197 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 21 , 29 , -1 , -1 , -1 , 102 , 102 , 102 , 102 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , -1 , -1 , 60 , 68 , 190 , 198 , 198 , 198 , 198 , -1 , -1 , -1 , 153 , 161 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , -1 , -1 , 61 , 69 , 191 , 199 , 199 , 199 , 199 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , 154 , 162 , 66 , 16 , 24 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , -1 , -1 , 61 , 69 , 208 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 270 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 2 , 10 , -1 , -1 , 102 , 102 , 102 , 102 , -1 , -1 , 191 , 199 , 207 , 214 , 222 , 230 , -1 , -1 , -1 , -1 , 61 , 69 , 208 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 262 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 10 , -1 , 11 , -1 , -1 , 102 , 102 , 102 , 102 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 61 , 69 , 208 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , 154 , 162 , 66 , 16 , 24 , -1 , -1 , -1 , -1 , -1 , 102 , 102 , 102 , 102 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 62 , 70 , 208 , -1 , -1 , -1 , -1 , 122 , 130 , 138 , 146 , -1 , -1 , -1 , -1 , 270 , -1 , 185 , 201 , 36 , 33 , 41],\n[ 9 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 123 , 131 , 139 , 147 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 209 , 209 , 209 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 324 , 324 , 209 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 300 , 308 , 209 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , 153 , 160 , 209 , 209 , 209 , 209 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 209 , 209 , 209 , 209 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 100 , 100 , 209 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 120 , 128 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 7 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 209 , 209 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 121 , 129 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , 19 , 27 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 122 , 130 , 138 , 146 , -1 , -1 , -1 , -1 , -1 , -1 , 5 , 13 , -1 , -1 , 314 , 322 , -1 , -1 , 347 , 347 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , 10 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 123 , 131 , 139 , 147 , -1 , -1 , -1 , -1 , -1 , -1 , 6 , 14 , -1 , -1 , 315 , 323 , -1 , -1 , 347 , 347 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , 19 , 27 , -1 , -1 , 22 , 30 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 7 , -1 , 314 , 322 , -1 , -1 , 347 , 347 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , 11 , -1 , -1 , 23 , 31 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 7 , -1 , -1 , -1 , 315 , 323 , -1 , -1 , 347 , 347 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , 19 , 27 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 314 , 322 , -1 , -1 , 347 , 347 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 347 , 347 , -1 , -1 , 347 , 347 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 343 , 351 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 189 , 197 , 197 , 197 , 205 , 212 , 220 , 228 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 189 , 197 , 205 , 213 , 221 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 53 , -1 , -1 , -1 , -1 , 343 , 351 , -1 , -1 , 20 , 28 , 333 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 12 , -1 , -1 , 341 , 339 , 339 , -1 , 189 , 197 , 197 , 205 , 208 , 208 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , 213 , 221 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , 350 , -1 , 20 , 28 , 101 , -1 , 337 , 331 , 337 , -1 , 190 , 198 , 198 , 206 , 208 , 326 , 334 , 63 , 71 , -1 , 190 , 198 , 206 , 213 , 221 , 198 , 206 , 213 , 221 , 229 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , 343 , 351 , -1 , -1 , -1 , -1 , 337 , 337 , 331 , -1 , 190 , 198 , 198 , 206 , 208 , 300 , 308 , -1 , -1 , -1 , 191 , 199 , 207 , 213 , 221 , 198 , 206 , 326 , 334 , 63 , 71 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 331 , 337 , 331 , -1 , 190 , 198 , 198 , 206 , 152 , 208 , 309 , -1 , -1 , -1 , -1 , -1 , 191 , 199 , 199 , 199 , 207 , 214 , 222 , 230 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 340 , 340 , 340 , -1 , 190 , 198 , 198 , 206 , 208 , 152 , 302 , 19 , 27 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 36 , 33 , 41],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 53 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 198 , 198 , 208 , 208 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 186 , 202 , 37 , 34 , 42],\n],];\n\nexport const objmap = [\n[\n[ 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , 200 , 243 , 251 , 259],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 116 , 124 , 132 , -1 , -1 , 94 , 94 , 94 , 172 , 180 , 19 , 27 , -1 , 200 , 35 , -1 , -1 , -1 , -1 , -1 , 94 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 244 , 252 , 260],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 117 , 125 , 133 , 141 , 149 , 94 , 94 , 94 , 173 , 181 , -1 , -1 , -1 , 201 , 36 , -1 , -1 , -1 , -1 , 172 , 180 , -1 , -1 , -1 , -1 , -1 , 105 , 113 , -1 , 105 , 113 , -1 , 105 , 113 , -1 , -1 , -1 , 185 , 201 , 245 , 253 , 261],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 118 , 126 , 134 , 142 , 150 , 94 , 94 , 94 , -1 , 326 , 334 , -1 , -1 , 202 , 37 , -1 , -1 , -1 , -1 , 173 , 181 , -1 , -1 , 105 , 113 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , 240 , 248],\n[ -1 , -1 , -1 , -1 , 159 , 167 , -1 , -1 , 119 , 127 , 135 , 143 , -1 , 94 , 94 , 94 , 97 , 136 , 94 , -1 , -1 , 94 , 94 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 98 , 106 , 114 , -1 , -1 , -1 , 185 , 201 , -1 , 241 , 249],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 154 , 162 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 94 , 94 , 94 , 94 , 94 , 94 , 94 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , 242 , 250],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 326 , 334 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 98 , 106 , 114 , -1 , -1 , -1 , 122 , 130 , 138 , 146 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 186 , 202 , 37 , 34 , 42],\n[ -1 , -1 , -1 , -1 , 93 , 93 , 93 , 93 , 93 , 154 , 162 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 123 , 131 , 139 , 147 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ 252 , 251 , -1 , 243 , 251 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ 252 , 252 , 260 , 244 , 252 , 260 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 98 , 106 , 114 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 98 , 106 , 114 , -1 , -1 , -1 , 184 , 200 , 304 , 32 , 40],\n[ 253 , 253 , 261 , 245 , 253 , 261 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 189 , 197 , 197 , 205 , -1 , -1 , 19 , 27 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , -1 , 240],\n[ -1 , 240 , 248 , -1 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , -1 , -1 , 197 , 197 , 197 , 197 , 197 , 205 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , -1 , -1 , 241],\n[ -1 , 241 , 249 , 257 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , -1 , -1 , 199 , 199 , 199 , 199 , 199 , 207 , 197 , 205 , 97 , 136 , -1 , -1 , 105 , 113 , -1 , 105 , 113 , -1 , 105 , 113 , -1 , -1 , -1 , 185 , 201 , 152 , -1 , 242],\n[ -1 , 242 , 250 , 258 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 191 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 197 , 197 , 197 , 197 , 197 , 197 , 197 , 205 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 189 , 197 , 205 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 199 , 199 , 199 , 199 , 199 , 199 , 198 , 206 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , 243 , 251],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , 153 , 161 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , 198 , 206 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , -1 , 244 , 252],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 93 , 93 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , -1 , -1 , -1 , -1 , -1 , 120 , 128 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , 198 , 206 , 197 , 197 , 197 , 197 , 197 , 197 , 197 , 197 , 185 , 201 , 152 , 245 , 253],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 93 , 93 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , -1 , -1 , -1 , -1 , -1 , 121 , 129 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , 198 , 206 , 199 , 199 , 199 , 199 , 199 , 199 , 199 , 199 , 185 , 201 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 93 , 93 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , 105 , 113 , 190 , 198 , 206 , -1 , -1 , 116 , 124 , 132 , -1 , -1 , 41 , -1 , 189 , 197 , 197 , 197 , 197 , 198 , 206 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , 251 , 251259],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 93 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 206 , 153 , 161 , 117 , 125 , 133 , 141 , 149 , -1 , -1 , 190 , 198 , 198 , 198 , 198 , 198 , 206 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , -1 , 252 , 252260],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , 122 , 130 , 138 , 146 , -1 , -1 , 190 , 198 , 206 , -1 , -1 , 118 , 126 , 134 , 142 , 150 , -1 , -1 , 191 , 199 , 199 , 199 , 199 , 199 , 207 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , 253 , 253261],\n[ -1 , -1 , 93 , 94 , 93 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , 123 , 131 , 139 , 147 , -1 , -1 , 190 , 198 , 206 , 153 , 161 , 119 , 127 , 135 , 143 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , -1 , -1 , 240],\n[ -1 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 191 , 199 , 207 , 214 , 222 , 230 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , -1 , 241],\n[ -1 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 105 , 113 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 243 , 251 , -1 , -1 , -1 , -1 , 96 , 104 , 112 , -1 , -1 , 185 , 201 , -1 , -1 , 242],\n[ -1 , -1 , 93 , 93 , 94 , 93 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 244 , 252 , 260 , -1 , -1 , -1 , -1 , 314 , 322 , -1 , -1 , 185 , 201 , 152 , -1 , 240],\n[ -1 , -1 , -1 , -1 , -1 , 93 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 96 , 104 , 112 , 41 , -1 , -1 , -1 , -1 , -1 , 245 , 253 , 261 , -1 , -1 , -1 , -1 , 315 , 323 , -1 , -1 , 185 , 201 , -1 , -1 , 241],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 94 , 21 , 29 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 314 , 322 , -1 , -1 , 185 , 201 , 152 , -1 , 242],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 21 , 29 , -1 , -1 , -1 , 116 , 124 , 132 , 140 , 105 , 113 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 315 , 323 , -1 , -1 , 185 , 201 , -1 , 240 , 248],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 94 , -1 , -1 , -1 , -1 , -1 , 117 , 125 , 133 , 141 , 149 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 314 , 322 , -1 , -1 , 185 , 201 , 152 , 241 , 249],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 94 , -1 , -1 , -1 , -1 , -1 , 118 , 126 , 134 , 142 , 150 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 315 , 323 , -1 , -1 , 185 , 201 , -1 , 242 , 250],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 94 , -1 , -1 , -1 , -1 , -1 , 119 , 127 , 135 , 143 , 105 , 113 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 314 , 322 , -1 , -1 , 185 , 201 , 152 , -1 , 243],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 96 , 104 , 112 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , -1 , -1 , 244],\n[ -1 , -1 , -1 , -1 , -1 , 48 , 56 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 240 , 248 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , -1 , 245],\n[ -1 , -1 , -1 , -1 , -1 , 49 , 57 , 19 , 27 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 241 , 249 , 257 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , -1 , -1 , 243],\n[ -1 , -1 , -1 , -1 , -1 , 49 , 57 , 16 , 24 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 242 , 250 , -1 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , -1 , 244],\n[ -1 , -1 , -1 , -1 , -1 , 49 , 57 , -1 , -1 , -1 , -1 , -1 , -1 , 15 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , -1 , -1 , 245],\n[ -1 , -1 , -1 , -1 , -1 , 49 , 57 , -1 , -1 , -1 , 15 , -1 , 5 , 13 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , 49 , 57 , -1 , -1 , -1 , -1 , -1 , 6 , 14 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 96 , 104 , 112 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , -1 , -1 , 243],\n[ -1 , -1 , -1 , -1 , 48 , 57 , 57 , -1 , -1 , -1 , 5 , 13 , 15 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 314 , 322 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , -1 , 244],\n[ -1 , -1 , -1 , -1 , 49 , 57 , 19 , 27 , -1 , -1 , 6 , 14 , -1 , 15 , -1 , -1 , 22 , 30 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 315 , 323 , 41 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , -1 , -1 , 245],\n[ -1 , -1 , -1 , -1 , 49 , 57 , 57 , -1 , -1 , -1 , 15 , -1 , -1 , -1 , -1 , -1 , 23 , 31 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 314 , 322 , 41 , -1 , -1 , -1 , -1 , 189 , 197 , 197 , 197 , 205 , 212 , 220 , 228 , -1 , -1 , 185 , 201 , 152 , -1 , 243],\n[ -1 , -1 , -1 , -1 , 49 , 57 , 57 , 20 , 28 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 16 , 24 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 315 , 323 , 41 , -1 , -1 , 189 , 197 , 205 , -1 , -1 , -1 , -1 , 171 , 301 , 229 , -1 , -1 , 185 , 201 , -1 , -1 , 244],\n[ -1 , -1 , -1 , -1 , 49 , 57 , 57 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 19 , 27 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 350 , 41 , -1 , -1 , 190 , -1 , -1 , 153 , 161 , -1 , -1 , -1 , -1 , 229 , -1 , -1 , 185 , 201 , 152 , -1 , 245],\n[ -1 , -1 , -1 , -1 , 49 , 57 , 57 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 79 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 41 , -1 , -1 , 190 , -1 , -1 , -1 , 17 , 25 , -1 , 171 , 179 , 229 , -1 , -1 , 185 , 201 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , 49 , 57 , 57 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 189 , 197 , 197 , 205 , 35 , 32 , 32 , 40 , -1 , -1 , 190 , -1 , -1 , -1 , 18 , 26 , -1 , -1 , -1 , 230 , -1 , -1 , 185 , 201 , 152 , -1 , -1],\n[ -1 , -1 , -1 , -1 , 49 , 57 , 57 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 85 , 20 , 28 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 198 , 206 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , -1 , -1 , 153 , 161 , -1 , -1 , 213 , 221 , 229 , -1 , -1 , 185 , 201 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , 49 , 57 , 57 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 198 , 206 , -1 , -1 , -1 , 41 , -1 , -1 , 191 , 199 , 207 , -1 , -1 , -1 , 206 , -1 , -1 , -1 , -1 , -1 , 185 , 201 , 152 , -1 , -1],\n[ -1 , -1 , -1 , -1 , 49 , 57 , 57 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 198 , 206 , -1 , -1 , -1 , 41 , -1 , -1 , -1 , -1 , 191 , 199 , 199 , 199 , 207 , 214 , 222 , 230 , -1 , -1 , 185 , 201 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , 50 , 58 , 58 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 198 , 206 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 4 , 4 , 185 , 201 , -1 , -1 , -1],\n[ 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 192 , 193 , 193 , -1 , 192 , 192192],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 259 , 252 , 252],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 184 , 192 , 216 , 92 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 259 , 244 , 252],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 185 , 193 , 316 , 324 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 259 , 245 , 253],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 186 , 194 , 216 , 324 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 189 , 197 , 205 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 190 , 198 , 197 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 191 , 199 , 199 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 189 , 189 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 159 , 167 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 159 , 167 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 240 , 248 , 256],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 241 , 249 , 257],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 242 , 250 , 258],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 156 , 164 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 243 , 251],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 240 , 248 , 256 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 244 , 252],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 241 , 249 , 257 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 245 , 253],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 243 , 251 , 259 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 242 , 250 , 258 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 244 , 252 , 260 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 245 , 253 , 261 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 243 , 251 , 259 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 244 , 252 , 260 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 245 , 253 , 261 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 243 , 251],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 17 , 25 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 240 , 248 , 256 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 244 , 252],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 18 , 26 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 241 , 249 , 257 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 245 , 253],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 242 , 250 , 258 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 77 , 85 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 16 , 24 , 25 , -1 , -1 , -1 , 78 , 86 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 18 , 26 , -1 , 54 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 55 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 240],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 241],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 20 , 28 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 17 , 25 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 171 , 179 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 242],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 20 , 28 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 18 , 26 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 243 , 251 , 259 , -1 , 243 , 251 , 259 , -1],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 243 , 251 , 244 , 252 , 260 , -1 , 244 , 252 , 260 , -1],\n],];\n"
  },
  {
    "path": "src/editor/maps/serene.js",
    "content": "// Map generated by assettool.js [Tue Sep 19 2023 09:13:08 GMT-0700 (Pacific Daylight Time)]\n\nexport const tilesetpath = \"./tilesets/Serene.png\"\nexport const tiledim = 32\nexport const screenxtiles = 19\nexport const screenytiles = 45\nexport const tilesetpxw = 608\nexport const tilesetpxh = 1440\n\nexport const bgtiles = [\n   [\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ],\n],\n[\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 11 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 49 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 133 , 152 , 152 , 152 , 152 , 152 , 152 , 152 , 152 , 152 , 190 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 79 , 98 , 117 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 80 , 99 , 118 , 123 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 84 , 103 , 122 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 43 , 24 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 65 , 26 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 65 , 26 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 46 , 46 , 65 , 26 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 45 , 65 , 27 , 46 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 123 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 65 , 28 , 47 , 173 , 192 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 41 , 60 , 60 , 174 , 193 , -1 , -1 , -1 , 12 , 31 , 31 , 123 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 79 , 98 , 117 , 123 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 80 , 99 , 118 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 81 , 100 , 119 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 82 , 101 , 120 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 134 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 153 , 191 , -1 , -1 , -1 , 12 , 31 , 83 , 102 , 121 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , 138 , 157 , 157 , 157 , 157 , 157 , 157 , 157 , 157 , 157 , 195 , -1 , -1 , -1 , 12 , 31 , 84 , 103 , 122 , 31 , 123 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 12 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 47 , 47 , 73 , 92 , 111 , 130 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 48 , 55 , 74 , 93 , 112 , 131 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 44 , 56 , 75 , 94 , 113 , 132 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 50 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 13 , 32 , 32 , 32 , 32 , 32 , 32 , 32 , 32 , 32 , 51 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],];\n\nexport const objmap = [\n[\n[ 180 , 199 , 218 , -1 , -1 , 356 , 375 , 394 , -1 , -1 , -1 , -1 , -1 , 286 , 294 , 313 , 332 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 181 , 200 , 219 , -1 , -1 , 357 , 376 , 395 , -1 , -1 , -1 , -1 , -1 , -1 , 295 , 314 , 333 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 182 , 201 , 220 , -1 , -1 , 358 , 377 , 396 , -1 , -1 , -1 , -1 , -1 , -1 , 296 , 315 , 334 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 183 , 202 , 221 , -1 , -1 , 359 , 378 , 397 , -1 , -1 , -1 , -1 , -1 , -1 , 297 , 316 , 335 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 184 , 203 , 222 , -1 , -1 , 360 , 379 , 398 , -1 , -1 , -1 , -1 , -1 , -1 , 298 , 317 , 336 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 185 , 204 , 223 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 186 , 205 , 224 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 187 , 206 , 225 , -1 , -1 , -1 , 294 , 313 , 332 , 356 , 375 , 394 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 180 , 199 , 218 , -1 , -1 , -1 , 295 , 314 , 333 , 357 , 376 , 395 , -1 , 285 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 181 , 200 , 219 , -1 , -1 , -1 , 296 , 315 , 334 , 358 , 377 , 396 , 268 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 232 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 182 , 201 , 220 , -1 , -1 , -1 , 297 , 316 , 335 , 359 , 378 , 397 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 183 , 202 , 221 , -1 , -1 , -1 , 298 , 317 , 336 , 360 , 379 , 398 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 184 , 203 , 222 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 185 , 204 , 223 , -1 , -1 , -1 , 289 , 308 , 308 , 308 , 327 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 186 , 205 , 224 , -1 , -1 , -1 , 290 , 309 , 309 , 309 , 328 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 187 , 206 , 225 , -1 , -1 , -1 , 290 , 309 , 309 , 309 , 312 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 180 , 199 , 218 , -1 , -1 , -1 , 290 , 309 , 309 , 309 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 181 , 200 , 219 , -1 , -1 , -1 , 290 , 309 , 309 , 309 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 182 , 201 , 220 , -1 , -1 , -1 , 290 , 309 , 309 , 309 , 328 , -1 , -1 , -1 , -1 , -1 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 183 , 202 , 221 , -1 , -1 , -1 , 291 , 310 , 310 , 310 , 329 , -1 , -1 , -1 , -1 , -1 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 184 , 203 , 222 , -1 , -1 , -1 , -1 , 232 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 185 , 204 , 223 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 264 , 283 , -1 , -1 , -1 , -1 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 186 , 205 , 224 , -1 , -1 , -1 , -1 , -1 , 235 , -1 , 265 , 284 , -1 , -1 , -1 , -1 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 187 , 206 , 225 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 180 , 199 , 218 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 181 , 200 , 219 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 235 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 182 , 201 , 220 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 351 , 370 , 389 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 183 , 202 , 221 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 352 , 371 , 390 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 184 , 203 , 222 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 353 , 372 , 391 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 185 , 204 , 223 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 354 , 373 , 392 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 186 , 205 , 224 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 355 , 374 , 393 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 187 , 206 , 225 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 180 , 199 , 218 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 181 , 200 , 219 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 182 , 201 , 220 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 183 , 202 , 221 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 184 , 203 , 222 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 185 , 204 , 223 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 186 , 205 , 224 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 187 , 206 , 225 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 180 , 199 , 218 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 181 , 200 , 219 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 182 , 201 , 220 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 183 , 202 , 221 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 184 , 203 , 222 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 185 , 204 , 223 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 186 , 205 , 224 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 187 , 206 , 225 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 180 , 199 , 218 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 181 , 200 , 219 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 182 , 201 , 220 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 183 , 202 , 221 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 184 , 203 , 222 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 185 , 204 , 223 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 186 , 205 , 224 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 187 , 206 , 225 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 180 , 199 , 218 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 181 , 200 , 219 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 182 , 201 , 220 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 183 , 202 , 221 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 184 , 203 , 222 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 185 , 204 , 223 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 186 , 205 , 224 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ 187 , 206 , 225 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],\n[\n[ -1 , -1 , 351 , 370 , 389 , -1 , -1 , 294 , 313 , 332 , 241 , 260 , 279 , -1 , -1 , -1 , -1 , 264 , 283 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , 352 , 371 , 390 , -1 , -1 , 295 , 314 , 333 , 242 , 261 , 280 , -1 , -1 , -1 , -1 , 265 , 284 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , 353 , 372 , 391 , -1 , -1 , 296 , 315 , 334 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , 354 , 373 , 392 , -1 , -1 , 297 , 316 , 335 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , 355 , 374 , 393 , -1 , -1 , 298 , 317 , 336 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 414 , 433 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 415 , 434 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 416 , 435 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , 417 , 436 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 713 , 732 , 751 , 770 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 714 , 733 , 752 , 771 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 715 , 734 , 753 , 772 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 716 , 735 , 754 , 773 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , 717 , 736 , 755 , 774 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 356 , 375 , 394 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 357 , 376 , 395 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 358 , 377 , 396 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 359 , 378 , 397 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 360 , 379 , 398 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n[ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ],\n],];\n"
  },
  {
    "path": "src/editor/se.html",
    "content": "<html>\n<head>\n<style>\n    table, th, td {\n        border: 1px solid black;\n        border-radius: 10px;\n    }\n </style>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n</head>\n<body>\n    <script src=\"se.js\" type=\"module\"></script>\n\n    <div class=\"tab\">\n        <button class=\"tablinks\" onclick=\"onTab(event, 'edit')\">edit</button>\n        <button class=\"tablinks\" onclick=\"onTab(event, 'map')\">map</button>\n        <button class=\"tablinks\" onclick=\"onTab(event, 'config')\">config</button>\n      </div>\n\n<div id=\"edit\" class=\"tabcontent\">\n    <TABLE WIDTH=\"100%\"><TR>\n        <TD ALIGN=\"center\"> <!-- first layer of map tiles -->\n          <div ID=\"layer0pane\" style=\"max-height: 480px;max-width:640px;overflow: scroll;\">\n          <canvas ID=\"level0\" height=\"1600px\" width=\"1600px\" ></canvas> </div>   \n        </TD>\n        <TD ALIGN=\"center\"> <!-- tile set-->\n            <div ID=\"tilesetpane\" style=\"max-height: 600;max-width:800;overflow: scroll;\">\n            <CANVAS ID=\"tileset\" width=\"5632px\" height=\"8672px\" STYLE=\"border: 1px dotted white;\"></CANVAS> </div>   \n         </TD>\n         </TR>\n\n        <tr>\n        <TD ALIGN=\"center\"> <!-- second layer of map tiles -->\n        <div ID=\"layer1pane\" style=\"max-height: 480px;max-width:640px;overflow: scroll;\">\n            <canvas ID=\"level1\" height=\"1600px\" width=\"1600px\" ></canvas>\n        </div></TD>\n        <TD ALIGN=\"center\"> <!-- aggregate map view -->\n        <div ID=\"compositepane\" style=\"max-height: 480px;max-width:640px;overflow: scroll;\">\n            <canvas ID=\"composite\" height=\"1600px\" width=\"1600px\" ></canvas>\n        </div></TD>\n        </tr>\n\n        <tr>\n        <TD ALIGN=\"center\"> <!-- object layer of map -->\n        <div ID=\"layer2pane\" style=\"max-height: 480px;max-width:640px;overflow: scroll;\">\n            <canvas ID=\"level3\" height=\"1600px\" width=\"1600px\" ></canvas>\n        </div></TD>\n        <TD ALIGN=\"center\"> <!-- object layer of map -->\n        <div ID=\"layer3pane\" style=\"max-height: 480px;max-width:640px;overflow: scroll;\">\n            <canvas ID=\"level4\" height=\"1600px\" width=\"1600px\" ></canvas>\n        </div></TD>\n        </tr>\n    </TABLE>\n</div>\n\n<div id=\"map\" class=\"tabcontent\">\n        <canvas id=\"mapcanvas\" height=\"1600px\" width=\"1600px\" ></canvas>\n</div>\n\n<div id=\"config\" class=\"tabcontent\">\n            <table><tr>\n                <td></td>\n                <td>level</td>\n                <td>Composite</td>\n                <td></td>\n            </tr>\n            <tr>\n            <td>\n            <button onclick = \"saveCompositeAsImage()\" > Save Composite </button>\n            </td><td>\n            <input type=\"file\" multiple id=\"levelfile\">\n            </td><td>\n            <input type=\"file\" multiple id=\"compositepng\">\n            </td><td>\n            <form name=\"myForm\">\n                16 <input type=\"radio\" name=\"radioTiledim\"  value=\"16\" />\n                32 <input type=\"radio\" name=\"radioTiledim\"  value=\"32\" checked />\n              </form>\n              </td>\n            </tr>\n            <tr>\n                <td>Load Tileset</td>\n            </tr>\n            <tr>\n                <td> <input type=\"file\" multiple id=\"tilesetfile\"> </td>\n            </tr>\n            </table>\n</div>\n\n\n</body>\n</html>"
  },
  {
    "path": "src/editor/se.js",
    "content": "// --\n// Simple level editer. \n//\n// TODO: \n//  - <esc> clear selected_tiles\n// \n// Done:\n//  - Delete tiles\n//  - move magic numbers to context / initialization (zIndex, pane size etc.)\n//  - todo fudge factor on g_ctx.tileset \n//  - get rid of dangerous CONFIG.tiledim (use g_ctx.tileDim instead)\n//  - XXX create tilesetpadding for tilesets whos tiles are spaced (e.g. phantasy star II)\n//  - only use fudge to pick sprites rather than fudge and non\n//  - use g_ctx for g_ctx.tileset parameters instead of CONFIG (starting with initTilesetConfig) \n//  - todo print locations on screen\n//\n// \n// Keybindings:\n// <ctl>-z - undo\n// g - overlay 32x32 grid\n// s - generate .js file to move over to convex/maps/\n// m - place a semi-transparent red mask over all tiles. This helps find invisible tiles\n// d - hold while clicking a tile to delete\n// p - toggle between 16pixel and 32 pixel. \n// \n// Known bugs and annoyances\n//  - if deleting a tile while filter is on, filter isn't refreshed so need to toggle with \"m\"\n// --\n\nimport * as PIXI from 'pixi.js'\nimport { g_ctx }  from './secontext.js' // global context\nimport * as CONFIG from './seconfig.js' \nimport * as UNDO from './undo.js'\nimport * as SPRITEFILE from './spritefile.js'\nimport * as UI from './sehtmlui.js'\nimport { EventSystem } from '@pixi/events';\n\ng_ctx.debug_flag = true;\n\nfunction tileset_index_from_coords(x, y) {\n    let retme = x + (y*g_ctx.tilesettilew);\n    console.log(\"tileset_index_from_coord \",retme, x, y);\n    return retme; \n}\nfunction level_index_from_coords(x, y) {\n    // place 16px tiles in separate index space\n    let offset = (g_ctx.tiledimx == 16)? CONFIG.MAXTILEINDEX : 0;\n    let retme = x + (y*CONFIG.leveltilewidth) + offset; \n    return retme;\n}\nfunction tileset_index_from_px(x, y) {\n    let coord_x = Math.floor((x - g_ctx.tileset.fudgex) / (g_ctx.tiledimx + CONFIG.tilesetpadding));\n    let coord_y = Math.floor((y - g_ctx.tileset.fudgey) / (g_ctx.tiledimy+ CONFIG.tilesetpadding));\n\n    console.log(\"tileset_index_from_px \",x, y);\n\n    return tileset_index_from_coords(coord_x, coord_y); \n}\nfunction level_index_from_px(x, y) {\n    let coord_x = Math.floor(x / g_ctx.tiledimx);\n    let coord_y = Math.floor(y / g_ctx.tiledimy);\n    return level_index_from_coords(coord_x, coord_y); \n}\n\nfunction tileset_coords_from_index(index) {\n        let x = index % (g_ctx.tilesettilew);\n        let y = Math.floor(index / (g_ctx.tilesettilew));\n        console.log(\"tilesettilewidth: \",g_ctx.tilesettilew);\n        console.log(\"tileset_coords_from_index tile coords: \",index,x,y);\n        return [x,y];\n}\n\nfunction tileset_px_from_index(index) {\n        let ret = tileset_coords_from_index(index); \n        return [ret[0] * (g_ctx.tiledimx+CONFIG.tilesetpadding), ret[1] * (g_ctx.tiledimy+CONFIG.tilesetpadding)] ;\n}\n\n\n// return a sprite of size tileDim given (x,y) starting location\nfunction sprite_from_px(x, y) {\n\n    const bt = PIXI.BaseTexture.from(g_ctx.tilesetpath, {\n        scaleMode: PIXI.SCALE_MODES.NEAREST,\n    });\n    let w = g_ctx.tiledimx;\n    let h = g_ctx.tiledimy;\n    if(x + w > g_ctx.tilesetpxw) {\n        console.log(\"sprite_from_px: Warning, texture overrun, truncating\");\n        w = g_ctx.tilesetpxw - x;\n    }\n    if(y + h > g_ctx.tilesetpxh) {\n        console.log(\"sprite_from_px: Warning, texture overrun, truncating\");\n        y = g_ctx.tilesetpxh - h;\n    }\n    let texture = new PIXI.Texture(bt,\n                new PIXI.Rectangle(x, y, w, h) \n            );\n    return new PIXI.Sprite(texture);\n}\n\nfunction DragState() {\n    this.square  = new PIXI.Graphics();\n    this.tooltip = new PIXI.Text('', {\n        fontFamily: 'Courier',\n        fontSize: 12,\n        fill: 0xffffff,\n        align: 'center',\n    });\n    this.startx  = 0;\n    this.starty = 0;\n    this.endx   = 0;\n    this.endy   = 0;\n}\n\nclass LayerContext {\n\n    constructor(app, pane, num, mod = null) {\n        this.app = app;\n        this.scrollpane = pane;\n        this.num = num;\n        this.widthpx  = CONFIG.levelwidth;\n        this.heightpx = CONFIG.levelheight;\n\n\n        this.container = new PIXI.Container();\n        this.sprites = {};\n        this.composite_sprites = {};\n        this.dragctx = new DragState();\n        this.tilearray = Array.from(Array(CONFIG.leveltileheight), () => new Array().fill(null)); \n\n        app.stage.addChild(this.container);\n\n        this.mouseshadow    = new PIXI.Container();\n        this.mouseshadow.zIndex = CONFIG.zIndexMouseShadow; \n        this.lasttileindex  = -1; \n\n        this.fudgex = 0; // offset from 0,0\n        this.fudgey = 0;\n\n        this.square = new PIXI.Graphics();\n        this.square.beginFill(0x2980b9);\n        this.square.drawRect(0, 0, CONFIG.levelwidth, CONFIG.levelheight);\n        this.square.endFill();\n        this.square.eventMode = 'static';\n        this.container.addChild(this.square);\n\n        this.square.on('mousemove', onLevelMousemove.bind(this));\n        this.square.on('mouseover', onLevelMouseover.bind(this));\n        this.square.on('pointerout', onLevelMouseOut.bind(this))\n        this.square.on('pointerdown', onLevelPointerDown.bind(null, this))\n            .on('pointerup', onLevelDragEnd.bind(null, this))\n            .on('pointerupoutside', onLevelDragEnd.bind(null, this));\n\n        if (mod != null && !(mod === g_ctx)) {\n            this.loadFromMapFile(mod);\n        }\n    }\n\n    loadFromMapFile(mod) {\n        let tiles = [];\n        if (this.num == 0) {\n            tiles = mod.bgtiles[0];\n        } else if (this.num == 1) {\n            tiles = mod.bgtiles[1];\n        } else if (this.num == 2) {\n            tiles = mod.objmap[0];\n        } else if (this.num == 3) {\n            tiles = mod.objmap[1];\n        } else {\n            console.log(\"loadFromMapFile: Error unknow layer number\");\n            return;\n        }\n\n        for (let x = 0; x < tiles.length; x++) {\n            for (let y = 0; y < tiles[0].length; y++) {\n                if (tiles[x][y] != -1) {\n                    this.addTileLevelCoords(x, y, mod.DEFAULTILEDIMX, tiles[x][y]);\n                }\n            }\n        }\n    }\n\n    //  this will create a rectangle with an alpha channel for every square that has a sprite. This helps find \n    //  sprites that are purely transparent\n    drawFilter() {\n\n        if (typeof this.filtergraphics == 'undefined') {\n            this.filtertoggle = true;\n            this.filtergraphics = new PIXI.Graphics();\n            this.filtergraphics.zIndex = CONFIG.zIndexFilter;\n        }\n\n        if (this.filtertoggle) {\n\n            this.filtergraphics.beginFill(0xff0000, 0.3);\n            for (let i in this.sprites) {\n                let spr = this.sprites[i];\n                this.filtergraphics.drawRect(spr.x, spr.y, g_ctx.tiledimx, g_ctx.tiledimy);\n            }\n            this.filtergraphics.endFill();\n            this.container.addChild(this.filtergraphics);\n        }else{\n            this.filtergraphics.clear();\n            this.container.removeChild(this.filtergraphics);\n        }\n\n        this.filtertoggle = ! this.filtertoggle;\n    }\n\n    // add tile of \"index\" to Level at location x,y\n    addTileLevelCoords(x, y, dim, index) {\n        return this.addTileLevelPx(x * dim, y * dim, index);\n    }\n\n    // -- delete all sprites / textures on a given index\n    // will NOOP if no tile exists\n    deleteFromIndex(index) {\n        if(g_ctx.debug_flag){\n            console.log(\"deleteFromIndex \",index)\n        }\n        \n        if(this.sprites.hasOwnProperty(index)){\n            let ctile = this.sprites[index];\n            let row = Math.floor(ctile.y / g_ctx.tiledimy);\n            let col = Math.floor(ctile.x / g_ctx.tiledimx);\n            for(let x = 0; x < this.tilearray[row].length; x++){\n                if(this.tilearray[row][x].x == col * g_ctx.tiledimx){\n                    console.log(\"Removing texture from tilearray \",x,row);\n                    this.tilearray[row].splice(x, 1);\n                }\n            }\n\n            this.container.removeChild(this.sprites[index]);\n            delete this.sprites[index];\n            this.updateAnimatedTiles();\n        }\n\n    }\n\n    // add tile of \"index\" to Level at location x,y\n    addTileLevelPx(x, y, index) {\n        let xPx = x;\n        let yPx = y;\n\n        let ctile = null;\n        let ctile2 = null;\n\n        let pxloc = tileset_px_from_index(index);\n        ctile = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n        ctile.index = index;\n\n        // snap to grid\n        const dx = g_ctx.tiledimx;\n        const dy = g_ctx.tiledimy;\n        ctile.x = Math.floor(xPx / dx) * dx;\n        ctile.y = Math.floor(yPx / dy) * dy;\n        // Stuff tileset coords into ctile for writing to ts file\n        ctile.tspx = [pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey]; \n\n        let new_index = level_index_from_px(ctile.x, ctile.y);\n\n        if (g_ctx.debug_flag) {\n            console.log('addTileLevelPx ', this.num, ' ctile.x ', ctile.x, 'ctile.y ', ctile.y, \"index \", index, \"new_index\", new_index);\n        }\n\n        if (!g_ctx.dkey) {\n            this.container.addChild(ctile);\n        }\n\n        if (this.sprites.hasOwnProperty(new_index)) {\n            this.deleteFromIndex(new_index);\n        }\n\n        if (!g_ctx.dkey) {\n            this.tilearray[Math.floor(yPx / dy)].push(ctile);\n            this.sprites[new_index] = ctile;\n        } else if (typeof this.filtergraphics != 'undefined') {\n            this.filtergraphics.clear();\n            this.drawFilter();\n                this.drawFilter(); // do twice to get toggle back to original state\n        }\n\n        return new_index;\n    }\n\n    // --\n    // FIXME : currently just a naive loop. \n    // --\n    updateAnimatedTiles() {\n        console.log(\"updateAnimatedTiles\");\n        for (let row = 0; row < CONFIG.leveltileheight; row++) {\n            if (!this.tilearray[row][0]) {\n                continue;\n            }\n            let textures = [];\n            for (let x = 0; x < this.tilearray[row].length; x++) {\n                textures.push(this.tilearray[row][x].texture);\n            }\n            const as = new PIXI.AnimatedSprite(textures);\n            as.x = row * g_ctx.tiledimx;\n            as.y = this.num * g_ctx.tiledimy;\n            as.animationSpeed = .1;\n            as.autoUpdate = true;\n            as.play();\n            if (this.tilearray[row].hasOwnProperty('as')){\n                g_ctx.composite.container.removeChild(this.tilearray[row].as);\n            }\n            this.tilearray[row].as = as;\n            g_ctx.composite.container.addChild(as);\n        }\n    }\n} // class  LayerContext\n\nclass TilesetContext {\n\n    constructor(app, mod = g_ctx) {\n        this.app = app;\n        this.container = new PIXI.Container();\n\n\n        console.log(mod.tilesetpath);\n        const texture = PIXI.Texture.from(mod.tilesetpath);\n        const bg    = new PIXI.Sprite(texture);\n\n        this.widthpx  = g_ctx.tilesetpxw;\n        this.heightpx = g_ctx.tilesetpxh;\n\n        this.square = new PIXI.Graphics();\n        this.square.beginFill(0x2980b9);\n        this.square.drawRect(0, 0, mod.tilesetpxw, mod.tilesetpxh);\n        this.square.endFill();\n        this.square.eventMode = 'static';\n        this.container.addChild(this.square);\n        this.container.addChild(bg);\n        \n        this.app.stage.addChild(this.container);\n\n        this.fudgex = 0; // offset from 0,0\n        this.fudgey = 0;\n\n        this.dragctx = new DragState();\n\n        this.square.on('mousedown', function (e) {\n\n            g_ctx.tile_index = tileset_index_from_px(e.global.x, e.global.y); \n\n            if(g_ctx.debug_flag) {\n                console.log(\"g_ctx.tileset mouse down. index \"+g_ctx.tile_index);\n            }\n        });\n\n        this.square.on('pointerdown', onTilesetDragStart)\n                .on('pointerup', onTilesetDragEnd)\n                .on('pointerupoutside', onTilesetDragEnd);\n    }\n} // class TilesetContext\n\n\nclass CompositeContext {\n\n    constructor(app) {\n        this.app = app;\n        this.widthpx  = CONFIG.levelwidth;\n        this.heightpx = CONFIG.levelheight;\n\n        this.container = new PIXI.Container();\n        this.container.sortableChildren = true;\n        this.app.stage.addChild(this.container);\n        this.sprites = {};\n        this.circle = new PIXI.Graphics();\n        this.circle.zIndex = CONFIG.zIndexCompositePointer;\n\n        this.fudgex = 0; // offset from 0,0\n        this.fudgey = 0;\n\n        this.mouseshadow    = new PIXI.Container(); \n        this.mouseshadow.zIndex = CONFIG.zIndexMouseShadow; \n        this.lasttileindex  = -1; \n\n        this.square = new PIXI.Graphics();\n        this.square.beginFill(0x2980b9);\n        this.square.drawRect(0, 0, CONFIG.levelwidth, CONFIG.levelheight);\n        this.square.endFill();\n        this.square.eventMode = 'static';\n        this.container.addChild(this.square);\n\n        this.square.on('mousedown', onCompositeMousedown.bind(null, this));\n    }\n\n} // class CompositeContext\n\n\nfunction doimport (str) {\n    if (globalThis.URL.createObjectURL) {\n      const blob = new Blob([str], { type: 'text/javascript' })\n      const url = URL.createObjectURL(blob)\n      const module = import(url)\n      URL.revokeObjectURL(url) // GC objectURLs\n      return module\n    }\n    \n    const url = \"data:text/javascript;base64,\" + btoa(moduleData)\n    return import(url)\n  }\n\nfunction resetPanes() {\n    g_ctx.tiledimx = 16;\n    g_ctx.tiledimy = 16;\n\n    g_ctx.composite.container.removeChildren();\n    g_ctx.composite = new CompositeContext(g_ctx.composite_app);\n    g_ctx.tileset_app.stage.removeChildren()\n    g_ctx.tileset = new TilesetContext(g_ctx.tileset_app);\n    g_ctx.g_layer_apps[0].stage.removeChildren()\n    g_ctx.g_layers[0] = new LayerContext(g_ctx.g_layer_apps[0], document.getElementById(\"layer0pane\"), 0);\n    g_ctx.g_layer_apps[1].stage.removeChildren()\n    g_ctx.g_layers[1] = new LayerContext(g_ctx.g_layer_apps[1], document.getElementById(\"layer1pane\"), 1);\n    g_ctx.g_layer_apps[2].stage.removeChildren()\n    g_ctx.g_layers[2] = new LayerContext(g_ctx.g_layer_apps[2], document.getElementById(\"layer2pane\"), 2);\n    g_ctx.g_layer_apps[3].stage.removeChildren()\n    g_ctx.g_layers[3] = new LayerContext(g_ctx.g_layer_apps[3], document.getElementById(\"layer3pane\"), 3);\n\n    redrawGrid();\n}\n\nfunction downloadpng(filename) {\n    let newcontainer = new PIXI.Container();\n    let children = [...g_ctx.composite.container.children];\n    for(let i = 0; i <  children.length; i++) {\n        let child = children[i];\n        if (! child.hasOwnProperty('isSprite') || !child.isSprite){\n            console.log(child);\n            continue;\n        }\n        // console.log(child, typeof child);\n        g_ctx.composite.container.removeChild(child);\n        newcontainer.addChild(child);\n    }\n      renderer.plugins.extract.canvas(newcontainer).toBlob(function (b) {\n      //renderer.plugins.extract.canvas(g_ctx.composite.container).toBlob(function (b) {\n      console.log(b);\n      var a = document.createElement(\"a\");\n      document.body.append(a);\n      a.download = filename;\n      a.href = URL.createObjectURL(b);\n      a.click();\n      a.remove();\n    }, \"image/png\");\n  }\n\nwindow.saveCompositeAsImage = () => {\n    downloadpng(\"g_ctx.composite.png\");\n}\n\nwindow.onTab = (evt, tabName) => {\n    // Declare all variables\n    var i, tabcontent, tablinks;\n\n    // Get all elements with class=\"tabcontent\" and hide them\n    tabcontent = document.getElementsByClassName(\"tabcontent\");\n    for (i = 0; i < tabcontent.length; i++) {\n        tabcontent[i].style.display = \"none\";\n    }\n\n    // Get all elements with class=\"tablinks\" and remove the class \"active\"\n    tablinks = document.getElementsByClassName(\"tablinks\");\n    for (i = 0; i < tablinks.length; i++) {\n        tablinks[i].className = tablinks[i].className.replace(\" active\", \"\");\n    }\n\n    // Show the current tab, and add an \"active\" class to the button that opened the tab\n    document.getElementById(tabName).style.display = \"block\";\n    evt.currentTarget.className += \" active\";\n\n    if (tabName == \"map\"){\n        map_app.stage.addChild(g_ctx.composite.container);\n    }else {\n        g_ctx.composite.app.stage.addChild(g_ctx.composite.container);\n    }\n}\n\nwindow.addEventListener(\n    \"keyup\", (event) => {\n        if (event.code == \"KeyD\"){\n            g_ctx.dkey = false;\n            g_ctx.g_layers.map( (l) => l.container.addChild(l.mouseshadow));\n            g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow);\n        }\n    });\nwindow.addEventListener(\n    \"keydown\", (event) => {\n\n        if (event.code == \"KeyD\"){\n            g_ctx.dkey = true;\n            g_ctx.g_layers.map((l) => l.container.removeChild(l.mouseshadow) );\n            g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow);\n        }\n\n        if (event.code == 'KeyS'){\n            SPRITEFILE.generate_sprite_file();\n        }\n        else if (event.code == 'Escape'){\n            g_ctx.selected_tiles = [];\n            g_ctx.g_layers.map((l) => l.mouseshadow.removeChildren());\n            composite.mouseshadow.removeChildren();\n        }\n        else if (event.code == 'KeyM'){\n            g_ctx.g_layers.map((l) => l.drawFilter () );\n        }else if (event.code == 'KeyP'){\n            setGridSize((g_ctx.tiledimx == 16)?32:16);\n        }\n        else if (event.ctrlKey && event.code === 'KeyZ'){\n            let undome = UNDO.undo_pop();\n            if (!undome) {\n                return;\n            }\n            let layer = undome.shift();\n            for(let i = 0; i < undome.length; i++) {\n                if (g_ctx.debug_flag) {\n                    console.log(\"Undo removing \", undome[i])\n                }\n                layer.container.removeChild(layer.sprites[undome[i]]);\n                g_ctx.composite.container.removeChild(layer.composite_sprites[undome[i]]);\n            }\n        }\n        else if (event.shiftKey && event.code == 'ArrowUp') {\n            g_ctx.tileset.fudgey -= 1;\n            redrawGridPane(g_ctx.tileset);\n        }\n        else if (event.shiftKey && event.code == 'ArrowDown') {\n            g_ctx.tileset.fudgey += 1;\n            redrawGridPane(g_ctx.tileset);\n        }\n        else if (event.shiftKey && event.code == 'ArrowLeft') {\n            g_ctx.tileset.fudgex -= 1;\n            redrawGridPane(g_ctx.tileset);\n        }\n        else if (event.shiftKey && event.code == 'ArrowRight') {\n            g_ctx.tileset.fudgex += 1;\n            redrawGridPane(g_ctx.tileset);\n        }\n     }\n  );\n\n// Listen to pointermove on stage once handle is pressed.\n\nfunction onTilesetDragStart(e)\n{\n    if (g_ctx.debug_flag) {\n        console.log(\"onDragStartTileset()\");\n    }\n    g_ctx.tileset.app.stage.eventMode = 'static';\n    g_ctx.tileset.app.stage.addEventListener('pointermove', onTilesetDrag);\n    \n    g_ctx.tileset.dragctx.startx = e.global.x;\n    g_ctx.tileset.dragctx.starty = e.global.y;\n    g_ctx.tileset.dragctx.endx = e.global.x;\n    g_ctx.tileset.dragctx.endy = e.global.y;\n\n    g_ctx.tileset.app.stage.addChild(g_ctx.tileset.dragctx.square);\n    // g_ctx.tileset.app.stage.addChild(g_ctx.tileset.dragctx.tooltip);\n\n    g_ctx.selected_tiles = [];\n}\n\n// Stop dragging feedback once the handle is released.\nfunction onTilesetDragEnd(e)\n{\n    if (g_ctx.debug_flag) {\n        console.log(\"onDragEndTileset()\");\n    }\n    g_ctx.tileset.app.stage.eventMode = 'auto';\n    g_ctx.tileset.app.stage.removeEventListener('pointermove', onTilesetDrag);\n    g_ctx.tileset.app.stage.removeChild(g_ctx.tileset.dragctx.square);\n    g_ctx.tileset.app.stage.removeChild(g_ctx.tileset.dragctx.tooltip);\n\n    let starttilex = Math.floor((g_ctx.tileset.dragctx.startx - g_ctx.tileset.fudgex) / g_ctx.tiledimx);\n    let starttiley = Math.floor((g_ctx.tileset.dragctx.starty - g_ctx.tileset.fudgey) / g_ctx.tiledimy);\n    let endtilex = Math.floor((g_ctx.tileset.dragctx.endx - g_ctx.tileset.fudgex) / g_ctx.tiledimx);\n    let endtiley = Math.floor((g_ctx.tileset.dragctx.endy - g_ctx.tileset.fudgey) / g_ctx.tiledimy);\n\n    if (g_ctx.debug_flag) {\n        console.log(\"sx sy ex ey \", starttilex, \",\", starttiley, \",\", endtilex, \",\", endtiley);\n    }\n    // let mouse clicked handle if there isn't a multiple tile square\n    if(starttilex === endtilex && starttiley === endtiley ){\n        return;\n    }\n\n    g_ctx.tile_index = tileset_index_from_px(e.global.x, e.global.y); \n\n    let origx = starttilex;\n    let origy = starttiley;\n    for(let y = starttiley; y <= endtiley; y++){\n        for(let x = starttilex; x <= endtilex; x++){\n            let squareindex = (y * g_ctx.tilesettilew) + x;\n            console.log(\"Pushing into st \", x - origx,y - origy, squareindex);\n            g_ctx.selected_tiles.push([x - origx,y - origy,squareindex]);\n        }\n    }\n\n    // if we're still 16x16 assume we're resizing the grid\n    if (g_ctx.tiledimx == 16 && g_ctx.tiledimy == 16) {\n        // for sprite tool, we want to set the tilesize based on the selected region\n        // FIXME .. don't use magic number 16\n        g_ctx.tiledimx = (1 + endtilex - starttilex) * 16;\n        g_ctx.tiledimy = (1 + endtiley - starttiley) * 16;\n        redrawGrid();\n        g_ctx.selected_tiles = [];\n        g_ctx.tile_index = tileset_index_from_px(g_ctx.tileset.dragctx.startx - g_ctx.tileset.fudgex, g_ctx.tileset.dragctx.starty - g_ctx.tileset.fudgey);\n    }\n\n    g_ctx.tileset.dragctx.square.clear();\n    // g_ctx.tileset.dragctx.tooltip.clear();\n}\n\nfunction onTilesetDrag(e)\n{\n    if (g_ctx.debug_flag) {\n        console.log(\"onDragTileset()\");\n    }\n    g_ctx.tileset.dragctx.endx = e.global.x;\n    g_ctx.tileset.dragctx.endy = e.global.y;\n    \n    g_ctx.tileset.dragctx.square.clear();\n    g_ctx.tileset.dragctx.square.beginFill(0xFF3300, 0.3);\n    g_ctx.tileset.dragctx.square.lineStyle(2, 0xffd900, 1);\n    g_ctx.tileset.dragctx.square.moveTo(g_ctx.tileset.dragctx.startx, g_ctx.tileset.dragctx.starty);\n    g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.endx, g_ctx.tileset.dragctx.starty);\n    g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.endx, g_ctx.tileset.dragctx.endy);\n    g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.startx, g_ctx.tileset.dragctx.endy);\n    g_ctx.tileset.dragctx.square.closePath();\n    g_ctx.tileset.dragctx.square.endFill();\n\n\n    // g_ctx.tileset.dragctx.tooltip.clear();\n    // g_ctx.tileset.dragctx.tooltip.beginFill(0xFF3300, 0.3);\n    // g_ctx.tileset.dragctx.tooltip.lineStyle(2, 0xffd900, 1);\n    // g_ctx.tileset.dragctx.tooltip.drawRect(e.global.x, e.global.y, 20,8);\n    // g_ctx.tileset.dragctx.tooltip.endFill();\n}\n\n//g_ctx.tileset.app.stage.addChild(g_ctx.tileset.container);\n\nfunction redrawGridPane(pane) {\n\n    if (typeof pane.gridgraphics != 'undefined') {\n        pane.container.removeChild(pane.gridgraphics);\n    }\n\n    pane.gridgraphics = new PIXI.Graphics();\n    let gridsizex = g_ctx.tiledimx;\n    let gridsizey = g_ctx.tiledimy;\n    pane.gridgraphics.lineStyle(1, 0x000000, 1);\n\n\n    let index = 0;\n    for (let i = 0; i < pane.widthpx; i += gridsizex) {\n        pane.gridgraphics.moveTo(i + pane.fudgex, 0 + pane.fudgey);\n        pane.gridgraphics.lineTo(i + pane.fudgex, pane.heightpx + pane.fudgey);\n        pane.gridgraphics.moveTo(i + gridsizex + pane.fudgex, 0 + pane.fudgey);\n        pane.gridgraphics.lineTo(i + gridsizex + pane.fudgex, pane.heightpx + pane.fudgey);\n\n    }\n    for (let j = 0; j < pane.heightpx; j += gridsizey) {\n        pane.gridgraphics.moveTo(0 + pane.fudgex, j + gridsizey + pane.fudgey);\n        pane.gridgraphics.lineTo(pane.widthpx + pane.fudgex, j + gridsizey + pane.fudgey);\n        pane.gridgraphics.moveTo(0 + pane.fudgex, j + pane.fudgey);\n        pane.gridgraphics.lineTo(pane.heightpx + pane.fudgex, j + pane.fudgey);\n    }\n\n    if (pane.gridvisible) {\n        pane.container.addChild(pane.gridgraphics);\n    }\n    pane.container.addChild(pane.gridgraphics);\n    pane.gridvisible = true;\n}\n\nfunction redrawGrid() {\n    g_ctx.g_layers.map((l) => redrawGridPane(l));\n    redrawGridPane(g_ctx.tileset);\n    redrawGridPane(g_ctx.composite);\n}\n\n\n// --\n// Variable placement logic Level1\n// --\n\nfunction centerCompositePane(x, y){\n    var compositepane = document.getElementById(\"compositepane\");\n    compositepane.scrollLeft = x - (CONFIG.htmlCompositePaneW/2);\n    compositepane.scrollTop  = y - (CONFIG.htmlCompositePaneH/2);\n}\n\nfunction centerLayerPanes(x, y){\n    // TODO remove magic number pulled from index.html\n    g_ctx.g_layers.map((l) => {\n        l.scrollpane.scrollLeft = x - (CONFIG.htmlCompositePaneW/2);\n        l.scrollpane.scrollTop  = y - (CONFIG.htmlCompositePaneH/2);\n      });\n}\n\nfunction onLevelMouseover(e) {\n    let x = e.data.global.x;\n    let y = e.data.global.y;\n    if(g_ctx.debug_flag){\n        console.log(\"onLevelMouseOver \",this.num);\n    }\n    if (x < this.scrollpane.scrollLeft || x > this.scrollpane.scrollLeft + CONFIG.htmlCompositePaneW) {\n        return;\n    }\n    if (y < this.scrollpane.scrollTop || y > this.scrollpane.scrollTop + CONFIG.htmlCompositePaneH) {\n        return;\n    }\n\n    if (this.lasttileindex != g_ctx.tile_index) {\n        this.mouseshadow.removeChildren(0);\n        g_ctx.composite.mouseshadow.removeChildren(0);\n        if (g_ctx.selected_tiles.length == 0) {\n            let shadowsprite = null;\n            let shadowsprite2 = null;\n\n            let pxloc = tileset_px_from_index(g_ctx.tile_index);\n            shadowsprite  = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n            shadowsprite2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n\n            shadowsprite.alpha = .5;\n            shadowsprite2.alpha = .5;\n            this.mouseshadow.addChild(shadowsprite);\n            g_ctx.composite.mouseshadow.addChild(shadowsprite2);\n        } else {\n            // TODO! adjust for fudge\n            for (let i = 0; i < g_ctx.selected_tiles.length; i++) {\n                let tile = g_ctx.selected_tiles[i];\n                console.log(\"TILE\", tile);\n                let pxloc = tileset_px_from_index(tile[2]);\n                console.log('PXLOC',pxloc);\n\n\n                const shadowsprite  = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n                const shadowsprite2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);\n                shadowsprite.x = tile[0] * g_ctx.tiledimx;\n                shadowsprite.y = tile[1] * g_ctx.tiledimy;\n                shadowsprite2.x = tile[0] * g_ctx.tiledimx;\n                shadowsprite2.y = tile[1] * g_ctx.tiledimy;\n                shadowsprite.alpha = .5;\n                shadowsprite2.alpha = .5;\n                this.mouseshadow.addChild(shadowsprite);\n                g_ctx.composite.mouseshadow.addChild(shadowsprite2);\n            }\n\n        }\n        this.mouseshadow.x = x - 16;\n        this.mouseshadow.y = y - 16;\n        this.container.removeChild(this.mouseshadow);\n        g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow);\n        this.container.addChild(this.mouseshadow);\n        g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow);\n    }\n\n    g_ctx.composite.app.stage.removeChild(g_ctx.composite.circle);\n    g_ctx.composite.app.stage.addChild(g_ctx.composite.circle);\n}\n\n\nfunction onLevelMouseOut(e) {\n    if (g_ctx.debug_flag) {\n        console.log(\"onLevelMouseOut \",this.num);\n    }\n    this.mouseshadow.removeChildren(0);\n    g_ctx.composite.mouseshadow.removeChildren(0);\n}\n\nfunction onLevelMousemove(e) {\n    let x = e.data.global.x;\n    let y = e.data.global.y;\n\n    // FIXME TEST CODE\n    this.mouseshadow.x = x-8;\n    this.mouseshadow.y = y-8;\n    g_ctx.composite.mouseshadow.x = x-8;\n    g_ctx.composite.mouseshadow.y = y-8;\n    // FIXME TEST CODE\n\n\n    if (x < this.scrollpane.scrollLeft || x > this.scrollpane.scrollLeft + CONFIG.htmlCompositePaneW) {\n        return;\n    }\n    if (y < this.scrollpane.scrollTop || y > this.scrollpane.scrollTop + CONFIG.htmlCompositePaneH) {\n        return;\n    }\n\n    g_ctx.composite.circle.clear();\n    g_ctx.composite.circle.beginFill(0xe50000, 0.5);\n    g_ctx.composite.circle.drawCircle(e.data.global.x, e.data.global.y, 3);\n    g_ctx.composite.circle.endFill();\n}\nfunction onCompositeMousedown(layer, e) {\n    if (g_ctx.debug_flag) {\n        console.log('onCompositeMouseDown: X', e.data.global.x, 'Y', e.data.global.y);\n    }\n\n    let xorig = e.data.global.x;\n    let yorig = e.data.global.y;\n\n    centerLayerPanes(xorig,yorig);\n}\n\n\n// Place with no variable target at destination\nfunction levelPlaceNoVariable(layer, e) {\n    if (g_ctx.debug_flag) {\n        console.log('levelPlaceNoVariable: X', e.data.global.x, 'Y', e.data.global.y);\n    }\n\n    let xorig = e.global.x;\n    let yorig = e.global.y;\n\n    // No need to center pane\n    // centerCompositePane(xorig,yorig);\n\n    if (g_ctx.dkey || g_ctx.selected_tiles.length == 0) {\n        let ti = layer.addTileLevelPx(e.global.x, e.global.y, g_ctx.tile_index);\n        UNDO.undo_add_single_index_as_task(layer, ti);\n    } else {\n        let undolist = [];\n        UNDO.undo_mark_task_start(layer);\n        for (let index of g_ctx.selected_tiles) {\n            let ti = layer.addTileLevelPx(xorig + index[0] * g_ctx.tiledimx, yorig + index[1] * g_ctx.tiledimy, index[2]);\n            UNDO.undo_add_index_to_task(ti);\n        }\n        UNDO.undo_mark_task_end();\n    }\n\n    layer.updateAnimatedTiles();\n}\n\n// Listen to pointermove on stage once handle is pressed.\nfunction onLevelPointerDown(layer, e)\n{\n    if (g_ctx.debug_flag) {\n        console.log(\"onLevelPointerDown()\");\n    }\n    layer.app.stage.eventMode = 'static';\n    layer.app.stage.addEventListener('pointermove', onLevelDrag.bind(null, layer, e));\n\n    layer.container.removeChild(layer.mouseshadow);\n    g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow);\n\n    layer.dragctx.startx = e.data.global.x;\n    layer.dragctx.starty = e.data.global.y;\n    layer.dragctx.endx = e.data.global.x;\n    layer.dragctx.endy = e.data.global.y;\n\n    layer.app.stage.addChild(layer.dragctx.square);\n    layer.app.stage.addChild(layer.dragctx.tooltip);\n}\n\nfunction onLevelDrag(layer, e)\n{\n    if(layer.dragctx.startx == -1){\n        layer.dragctx.square.clear();\n        return;\n    }\n\n    layer.dragctx.endx = e.global.x;\n    layer.dragctx.endy = e.global.y;\n\n    if (g_ctx.debug_flag) {\n        console.log(\"onLevelDrag()\");\n    }\n    \n    layer.dragctx.square.clear();\n    layer.dragctx.square.beginFill(0xFF3300, 0.3);\n    layer.dragctx.square.lineStyle(2, 0xffd900, 1);\n    layer.dragctx.square.moveTo(layer.dragctx.startx, layer.dragctx.starty);\n    layer.dragctx.square.lineTo(layer.dragctx.endx, layer.dragctx.starty);\n    layer.dragctx.square.lineTo(layer.dragctx.endx, layer.dragctx.endy);\n    layer.dragctx.square.lineTo(layer.dragctx.startx, layer.dragctx.endy);\n    layer.dragctx.square.closePath();\n    layer.dragctx.square.endFill();\n\n    const vwidth  = Math.floor((layer.dragctx.endx - layer.dragctx.startx)/g_ctx.tiledimx);\n    const vheight = Math.floor((layer.dragctx.endy - layer.dragctx.starty)/g_ctx.tiledimy);\n    layer.dragctx.tooltip.x = e.global.x + 16;\n    layer.dragctx.tooltip.y = e.global.y - 4;\n    layer.dragctx.tooltip.text = \"[\"+vwidth+\",\"+vheight+\"]\\n\"+\n                                 \"(\"+Math.floor(e.global.x/g_ctx.tiledimx)+\",\"+Math.floor(e.global.y/g_ctx.tiledimy)+\")\";\n    //layer.dragctx.tooltip.text = \"(\"+e.global.x+\",\"+e.global.y+\")\";\n}\n\nfunction onLevelCreateAnimatedSprite(row) {\n\n\n}\n\n// Stop dragging feedback once the handle is released.\nfunction onLevelDragEnd(layer, e)\n{\n    layer.dragctx.endx = e.data.global.x;\n    layer.dragctx.endy = e.data.global.y;\n\n    if(layer.dragctx.startx == -1){\n        console.log(\"onLevelDragEnd() start is -1 bailing\");\n        return;\n    }\n    if (g_ctx.debug_flag) {\n        console.log(\"onLevelDragEnd()\");\n    }\n\n    layer.container.addChild(layer.mouseshadow);\n    g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow);\n\n    layer.app.stage.eventMode = 'auto';\n    layer.app.stage.removeChild(layer.dragctx.square);\n    layer.app.stage.removeChild(layer.dragctx.tooltip);\n\n    let starttilex = Math.floor(layer.dragctx.startx / g_ctx.tiledimx);\n    let starttiley = Math.floor(layer.dragctx.starty / g_ctx.tiledimy);\n    let endtilex = Math.floor(layer.dragctx.endx / g_ctx.tiledimx);\n    let endtiley = Math.floor(layer.dragctx.endy / g_ctx.tiledimy);\n\n    if (g_ctx.debug_flag) {\n        console.log(\"sx \", starttilex, \" ex \", endtilex);\n        console.log(\"sy \", starttiley, \" ey \", endtiley);\n    }\n\n    // no variable placement. \n    if(starttilex === endtilex && starttiley == endtiley ){\n        levelPlaceNoVariable(layer, e);\n        layer.dragctx.startx = -1;\n        layer.dragctx.endx    = -1;\n        layer.dragctx.starty = -1;\n        layer.dragctx.endy    = -1;\n        return;\n    }\n\n    if (g_ctx.selected_tiles.length == 0) {\n        UNDO.undo_mark_task_start(layer);\n        for (let i = starttilex; i <= endtilex; i++) {\n            for (let j = starttiley; j <= endtiley; j++) {\n                let squareindex = (j * g_ctx.tilesettilew) + i;\n                let ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, g_ctx.tile_index);\n                UNDO.undo_add_index_to_task(ti);\n            }\n        }\n        UNDO.undo_mark_task_end();\n    } else {\n        // figure out selected grid\n        let selected_grid = Array.from(Array(64), () => new Array(64)); // FIXME ... hope 64x64 is enough\n        let row = 0;\n        let column = 0;\n        let selected_row = g_ctx.selected_tiles[0][1];\n        // selected_grid[0] = [];\n        for (let index of g_ctx.selected_tiles) {\n            // console.log(\"Selected row \", selected_row, index);\n            if(index[1] != selected_row){\n                selected_row = index[1];\n                row++;\n                column = 0;\n                //selected_grid[row] = [];\n            }\n            selected_grid[column++][row]  = index;\n        }\n        // at this point should have a 3D array of the selected tiles and the size should be row, column\n\n        UNDO.undo_mark_task_start(layer);\n\n        let ti=0;\n        for (let i = starttilex; i <= endtilex; i++) {\n            for (let j = starttiley; j <= endtiley; j++) {\n                let squareindex = (j * g_ctx.tilesettilew) + i;\n                if (j === starttiley) { // first row \n                    if (i === starttilex) { // top left corner\n                        ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[0][0][2]);\n                    }\n                    else if (i == endtilex) { // top right corner\n                        ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[column - 1][0][2]);\n                    } else { // top middle\n                        ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[1][0][2]);\n                    }\n                } else if (j === endtiley) { // last row\n                    if (i === starttilex) { // bottom left corner\n                        ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[0][row][2]);\n                    }\n                    else if (i == endtilex) { // bottom right corner\n                        ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[column - 1][row][2]);\n                    } else { // bottom middle\n                        ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[1][row][2]);\n                    }\n                } else { // middle row\n                    if (i === starttilex) { // middle left \n                        ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[0][(row > 0)? 1 : 0][2]);\n                    }\n                    else if (i === endtilex) { // middle end \n                        ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[column - 1][(row > 0)? 1 : 0][2]);\n                    } else { // middle middle\n                        ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[1][(row > 0)? 1 : 0][2]);\n                    }\n                }\n                UNDO.undo_add_index_to_task(ti);\n            }\n        }\n        UNDO.undo_mark_task_end();\n    }\n\n    layer.dragctx.square.clear();\n\n    layer.dragctx.startx = -1;\n    layer.dragctx.starty = -1;\n}\n\n\n\n// --\n// Initialized all pixi apps / components for application\n// --\nfunction initPixiApps() {\n\n    // -- Editor wide globals --\n\n    // First layer of level\n    const level_app0 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level0') });\n    let layer0 = new LayerContext(level_app0, document.getElementById(\"layer0pane\"), 0);\n\n    // second layer of level \n    const level_app1 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level1') });\n    let layer1 = new LayerContext(level_app1, document.getElementById(\"layer1pane\"), 1);\n\n    //  object layer of level\n    const level_app2 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level3') });\n    let layer2 = new LayerContext(level_app2, document.getElementById(\"layer2pane\"), 2);\n\n    //  object layer of level\n    const level_app3 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level4') });\n\n    let layer3 = new LayerContext(level_app3, document.getElementById(\"layer3pane\"), 3);\n\n    g_ctx.g_layer_apps = [];\n    g_ctx.g_layer_apps.push(level_app0 );\n    g_ctx.g_layer_apps.push(level_app1);\n    g_ctx.g_layer_apps.push(level_app2);\n    g_ctx.g_layer_apps.push(level_app3);\n\n\n    g_ctx.g_layers = [];\n    g_ctx.g_layers.push(layer0);\n    g_ctx.g_layers.push(layer1);\n    g_ctx.g_layers.push(layer2);\n    g_ctx.g_layers.push(layer3);\n\n    // g_ctx.composite view \n    g_ctx.composite_app = new PIXI.Application({ backgroundAlpha: 0, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('composite') });\n    g_ctx.composite = new CompositeContext(g_ctx.composite_app);\n\n    //  map tab \n    g_ctx.map_app = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('mapcanvas') });\n\n    // g_ctx.tileset\n    // g_ctx.tileset_app = new PIXI.Application({ width: g_ctx.tilesetpxw, height: g_ctx.tilesetpxh, view: document.getElementById('tileset') });\n    g_ctx.tileset_app = new PIXI.Application({ width: 5632 , height: 8672, view: document.getElementById('tileset') });\n    //g_ctx.tileset_app = new PIXI.Application({ backgroundColor: 0x2980b9, width: 5632 , height: 8672, view: document.getElementById('tileset') });\n\n    const { renderer } = g_ctx.tileset_app;\n    // Install the EventSystem\n    renderer.addSystem(EventSystem, 'tileevents');\n    g_ctx.tileset = new TilesetContext(g_ctx.tileset_app);\n}\n\n// --\n// Initialize handlers for file loading\n// --\n\nfunction initLevelLoader() {\n    let filecontent = \"\";\n\n    const fileInput = document.getElementById('levelfile');\n    fileInput.onchange = (evt) => {\n        if (!window.FileReader) return; // Browser is not compatible\n\n        var reader = new FileReader();\n\n        reader.onload = function (evt) {\n            if (evt.target.readyState != 2) return;\n            if (evt.target.error) {\n                alert('Error while reading file');\n                return;\n            }\n\n            filecontent = evt.target.result;\n            doimport(filecontent).then(mod => loadMapFromModule(mod));\n        };\n\n        reader.readAsText(evt.target.files[0]);\n    }\n}\n\n\nfunction setGridSize(size) {\n    if (size == 16) {\n        if (g_ctx.tiledimx == 16) { return; }\n        g_ctx.tilesettilew = (g_ctx.tilesettilew/ (size / g_ctx.tiledimx));\n        g_ctx.tilesettileh = (g_ctx.tilesettileh / (size / g_ctx.tiledimy));\n        g_ctx.tiledimx = 16;\n        g_ctx.dimlog = Math.log2(g_ctx.tiledimx);\n        g_ctx.curtiles = g_ctx.tiles16;\n        console.log(\"set to curTiles16\");\n    } else if (size == 32) {\n        if (g_ctx.tiledimx == 32) { return; }\n        g_ctx.tilesettilew = (g_ctx.tilesettilew/ (size / g_ctx.tiledimx));\n        g_ctx.tilesettileh = (g_ctx.tilesettileh / (size / g_ctx.tiledimy));\n        g_ctx.tiledimx = 32;\n        g_ctx.dimlog = Math.log2(g_ctx.tiledimx);\n        g_ctx.curtiles = g_ctx.tiles32;\n        console.log(\"set to curTiles32\");\n    } else {\n        console.debug(\"Invalid TileDim!\");\n        return;\n    }\n    redrawGrid();\n}\n\nfunction initRadios() {\n    var rad = document.myForm.radioTiledim;\n    var prev = null;\n    for (var i = 0; i < rad.length; i++) {\n        rad[i].addEventListener('change', function () {\n            if (this !== prev) {\n                prev = this;\n            }\n            setGridSize(this.value);\n        });\n    }\n}\n\n// --\n// Load in default tileset and use to set properties\n// --\nconst initTilesConfig = async (path = CONFIG.DEFAULTTILESETPATH) => {\n\n    g_ctx.tilesetpath = path; \n\n    return new Promise((resolve, reject) => {\n        \n    const texture = new PIXI.BaseTexture(g_ctx.tilesetpath);\n\n    console.log(\"Loading texture \",g_ctx.tilesetpath);\n    texture .on('loaded', function() {\n        // size of g_ctx.tileset in px\n        g_ctx.tilesetpxw = texture.width;\n        g_ctx.tilesetpxh = texture.height;\n        console.log(\"Texture size w:\", g_ctx.tilesetpxw, \"h:\", g_ctx.tilesetpxh);\n        // size of g_ctx.tileset in tiles\n        let tileandpad = g_ctx.tiledimx + CONFIG.tilesetpadding;\n        let numtilesandpadw = Math.floor(g_ctx.tilesetpxw / tileandpad);\n        g_ctx.tilesettilew = numtilesandpadw + Math.floor((g_ctx.tilesetpxw - (numtilesandpadw * tileandpad))/g_ctx.tiledimx);\n        let numtilesandpadh = Math.floor(g_ctx.tilesetpxh / tileandpad);\n        g_ctx.tilesettileh = numtilesandpadh + Math.floor((g_ctx.tilesetpxh - (numtilesandpadh * tileandpad))/g_ctx.tiledimy);\n        console.log(\"Number of x tiles \",g_ctx.tilesettilew,\" y tiles \",g_ctx.tilesettileh);\n\n        g_ctx.MAXTILEINDEX = g_ctx.tilesettilew * g_ctx.tilesettileh;\n\n        texture.destroy();\n        resolve();\n    });\n\n  \n      });\n  };\n\nfunction initTiles() {\n    // load g_ctx.tileset into a global array of textures for blitting onto levels\n    const bt = PIXI.BaseTexture.from(g_ctx.tilesetpath, {\n        scaleMode: PIXI.SCALE_MODES.NEAREST,\n    });\n    for (let x = 0; x < CONFIG.tilesettilewidth; x++) {\n        for (let y = 0; y < CONFIG.tilesettileheight; y++) {\n            g_ctx.tiles32[x + y * CONFIG.tilesettilewidth] = new PIXI.Texture(\n                bt,\n                new PIXI.Rectangle(x * 32, y * 32, 32, 32),\n            );\n        }\n    }\n    for (let x = 0; x < CONFIG.tilesettilewidth * 2; x++) {\n        for (let y = 0; y < CONFIG.tilesettileheight * 2; y++) {\n            g_ctx.tiles16[x + y * CONFIG.tilesettilewidth * 2] = new PIXI.Texture(\n                bt,\n                new PIXI.Rectangle(x * 16, y * 16, 16, 16),\n            );\n        }\n    }\n\n    g_ctx.curtiles = g_ctx.tiles32;\n}\n\nfunction newTilesetFromFile(){\n\n    initTilesConfig(g_ctx.tilesetpath).then(resetPanes);\n}\n\nasync function init() {\n\n    UI.initMainHTMLWindow();\n    await initTilesConfig(); // needs to be called before Pixi apps are initialized\n\n    initPixiApps();\n    initRadios();\n    initTiles();\n    initLevelLoader();\n    UI.initCompositePNGLoader();\n    UI.initTilesetLoader(newTilesetFromFile); \n\n    redrawGrid();\n}\n\ninit();"
  },
  {
    "path": "src/editor/seconfig.js",
    "content": "//export const DEFAULTTILESETPATH = \"./spritesheets/women.png\";\n//export const DEFAULTILEDIMX = 32; // px\n//export const DEFAULTILEDIMY = 34; // px\n\n//export const DEFAULTTILESETPATH = \"./spritesheets/doll.png\";\n//export const DEFAULTILEDIMX = 48; // px\n//export const DEFAULTILEDIMY = 48; // px\n\n// export const DEFAULTTILESETPATH = \"./spritesheets/peeps.png\";\n// export const DEFAULTILEDIMX = 48; // px\n// export const DEFAULTILEDIMY = 96; // px\n\nexport const DEFAULTTILESETPATH = \"./spritesheets/tall.png\";\n//export const DEFAULTTILESETPATH = \"./spritesheets/Clothes_Hanging_1_32x32.png\"\nexport const DEFAULTILEDIMX = 16; // px\nexport const DEFAULTILEDIMY = 16; // px\n\n// export const DEFAULTTILESETPATH = \"./spritesheets/wateranimate2.png\";\n// export const DEFAULTILEDIMX = 32; // px\n// export const DEFAULTILEDIMY = 32; // px\n\n\n// If there is padding between tilesets, set this to the pixel size\nexport const tilesetpadding = 0; \n\n\n// width / height of layer panes\nexport const levelwidth  = 2048; // px\nexport const levelheight = 1536; // px\n\nexport let leveltilewidth  = Math.floor(levelwidth / DEFAULTILEDIMX);\nexport let leveltileheight = Math.floor(levelheight / DEFAULTILEDIMX);\n\nexport const MAXTILEINDEX = leveltilewidth * leveltileheight;\n\n\n// -- HTML\n\nexport const htmlLayerPaneW = 800;\nexport const htmlLayerPaneH = 600;\n\nexport const htmlTilesetPaneW = 800;\nexport const htmlTilesetPaneH = 600;\n\nexport const htmlCompositePaneW = 800;\nexport const htmlCompositePaneH = 600;\n\n// --  zIndex\n\n// 1-10 taken by layers\nexport const zIndexFilter           =  20;\nexport const zIndexMouseShadow      =  30;\nexport const zIndexGrid             =  50;\nexport const zIndexCompositePointer =  100;\n"
  },
  {
    "path": "src/editor/secontext.js",
    "content": "import * as PIXI from 'pixi.js'\nimport * as CONFIG from './seconfig.js'\n\nvar ContextCreate = (function(){\n\n    function ContextSingleton() {\n        this.tilesetpxw = 0;\n        this.tilesetpxh = 0;\n        this.tilesettilew = 0;\n        this.tilesettileh = 0;\n        this.MAXTILEINDEX = 0;\n        this.tile_index = 0;\n        this.selected_tiles = []; // current set of selected tiles\n        this.tiledimx = CONFIG.DEFAULTILEDIMX ; // px\n        this.tiledimy = CONFIG.DEFAULTILEDIMY; // px\n        this.dimlog = Math.log2(this.tileDim);  //log2(TileDim)\n        this.dkey = false;   // is 'd' key depressed? (for delete)\n        this.tiles32  = [];  // all tiles from tilemap (32x32)\n        this.tiles16  = []; \n        this.fudgetiles = [];\n        this.g_layers = []; // level layers\n\n    }\n\n    var instance;\n    return {\n        getInstance: function(){\n            if (instance == null) {\n                instance = new ContextSingleton();\n                // Hide the constructor so the returned object can't be new'd...\n                instance.constructor = null;\n            }\n            return instance;\n        }\n   };\n})();\n\n// global shared state between all panes\nexport let g_ctx = ContextCreate.getInstance();"
  },
  {
    "path": "src/editor/sehtmlui.js",
    "content": "import * as PIXI from 'pixi.js'\nimport { g_ctx }  from './secontext.js' // global context\nimport * as CONFIG from './seconfig.js' \n\n// --\n//  Set sizes and limits for HTML in main UI\n// --\n\nexport function initMainHTMLWindow() {\n    document.getElementById(\"layer0pane\").style.maxWidth  = \"\"+CONFIG.htmlLayerPaneW+\"px\"; \n    document.getElementById(\"layer0pane\").style.maxHeight = \"\"+CONFIG.htmlLayerPaneH+\"px\"; \n    document.getElementById(\"layer1pane\").style.maxWidth  = \"\"+CONFIG.htmlLayerPaneW+\"px\"; \n    document.getElementById(\"layer1pane\").style.maxHeight = \"\"+CONFIG.htmlLayerPaneH+\"px\"; \n    document.getElementById(\"layer2pane\").style.maxWidth  = \"\"+CONFIG.htmlLayerPaneW+\"px\"; \n    document.getElementById(\"layer2pane\").style.maxHeight = \"\"+CONFIG.htmlLayerPaneH+\"px\"; \n    document.getElementById(\"layer3pane\").style.maxWidth  = \"\"+CONFIG.htmlLayerPaneW+\"px\"; \n    document.getElementById(\"layer3pane\").style.maxHeight = \"\"+CONFIG.htmlLayerPaneH+\"px\"; \n\n    document.getElementById(\"tilesetpane\").style.maxWidth  = \"\"+CONFIG.htmlTilesetPaneW+\"px\"; \n    document.getElementById(\"tilesetpane\").style.maxHeight = \"\"+CONFIG.htmlTilesetPaneH+\"px\";\n    document.getElementById(\"compositepane\").style.maxWidth  = \"\"+CONFIG.htmlCompositePaneW+\"px\"; \n    document.getElementById(\"compositepane\").style.maxHeight = \"\"+CONFIG.htmlCompositePaneH+\"px\";\n\n    // hide map tab\n    let mappane = document.getElementById(\"map\");\n    mappane.style.display = \"none\";\n}\n\n// --\n// Initialize handlers loading a PNG file into the composite window \n// --\n\nexport function initCompositePNGLoader() {\n    const fileInput = document.getElementById('compositepng');\n    fileInput.onchange = (evt) => {\n        if (!window.FileReader) return; // Browser is not compatible\n        if (g_ctx.debug_flag) {\n            console.log(\"compositepng \", fileInput.files[0].name);\n        }\n        let bgname = fileInput.files[0].name;\n\n        const texture = PIXI.Texture.from(\"./\"+bgname);\n        const bg      = new PIXI.Sprite(texture);\n        bg.zIndex = 0;\n        g_ctx.composite.container.addChild(bg);\n    }\n}\n// -- \n// initailized handler to load a new tileset \n// --\n\nexport function initTilesetLoader(callme) {\n    const fileInput = document.getElementById('tilesetfile');\n    fileInput.onchange = async (evt) => {\n        if (!window.FileReader) return; // Browser is not compatible\n        if (g_ctx.debug_flag) {\n            console.log(\"spritesheet \", fileInput.files[0].name);\n        }\n        g_ctx.tilesetpath =  \"./spritesheets/\"+fileInput.files[0].name;\n\n        g_ctx.tiledimx = 16;\n        g_ctx.tiledimy = 16;\n\n        callme();\n    }\n}\n"
  },
  {
    "path": "src/editor/spritefile.js",
    "content": "import * as CONFIG from './seconfig.js' \nimport * as UTIL from './eutils.js'\nimport { g_ctx }  from './secontext.js' // global context\n\n\nfunction generate_preamble() {\n    const preamble = '' +\n        '{\"frames\": {\\n' +\n        '\\n';\n       return preamble; \n}\n\n// Function to download data to a file\nfunction download(data, filename, type) {\n    var file = new Blob([data], {type: type});\n    if (window.navigator.msSaveOrOpenBlob) // IE10+\n        window.navigator.msSaveOrOpenBlob(file, filename);\n    else { // Others\n        var a = document.createElement(\"a\"),\n                url = URL.createObjectURL(file);\n        a.href = url;\n        a.download = filename;\n        document.body.appendChild(a);\n        a.click();\n        setTimeout(function() {\n            document.body.removeChild(a);\n            window.URL.revokeObjectURL(url);  \n        }, 0); \n    }\n}\n\nexport function generate_sprite_file() {\n\n    let layer0 = g_ctx.g_layers[0];\n    console.log(\"generate_sprite_file\");\n\n    let text = generate_preamble();\n\n    let animations = Array.from(Array(CONFIG.leveltileheight), () => new Array().fill(null)); \n\n    for (let row = 0; row < CONFIG.leveltileheight; row++) {\n        if (!layer0.tilearray[row][0]) {\n            // FIXME\n            // Assume row is empty if first tile is. \n            continue;\n        }\n\n        for (let x = 0; x < layer0.tilearray[row].length; x++) {\n\n        //\"pixels_large1.png\":\n        // {\n        // \t\"frame\": {\"x\":0,\"y\":192,\"w\":32,\"h\":64},\n        // \t\"rotated\": false,\n        // \t\"trimmed\": true,\n        // \t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":32,\"h\":64},\n        // \t\"sourceSize\": {\"w\":32,\"h\":64}\n        // },\n\n            let framename = '\"tile' + row + \"_\" + x + '\"';\n\n            animations[row].push(framename);\n            let frame = layer0.tilearray[row][x];\n            text += framename + \": { \\n\";\n            text += '\\t\"frame\": {';\n            text += '\"x\": '+ frame.tspx[0]+ ', \"y\": '+ frame.tspx[1]+ ', \"w\": '+ g_ctx.tiledimx+ ', \"h\": '+ g_ctx.tiledimy+ ' },\\n';\n            text += '\\t\"rotated\": false,\\n';\n            text += '\\t\"trimmed\": true,\\n';\n            text += '\\t\"spriteSourceSize\": {';\n            text += '\"x\":0, \"y\":0, \"w\": '+ g_ctx.tiledimx+ ', \"h\": '+ g_ctx.tiledimy+ ' },\\n';\n            text += '\\t\"sourceSize\": {';\n            text += '\"w\": '+ g_ctx.tiledimx+ ', \"h\": '+ g_ctx.tiledimy+ ' }\\n';\n            text += '\\t}';\n\n            text += (x === layer0.tilearray[row].length - 1)?  '\\n':',\\n'\n        }\n    }\n    text += '},\\n';\n    text += '\"animations\": {\\n';\n\n    for (let row = 0; row < CONFIG.leveltileheight; row++) {\n        if(animations[row].length == 0) {\n            continue;\n        }\n        text += '\"row'+row+'\" : [';\n        for (let x = 0; x < animations[row].length; x++){\n            text += ''+animations[row][x];\n            if (x < animations[row].length - 1){\n                text += ',';\n            }\n        }\n        text += \"],\\n\"\n    }\n\n    // remove the trailing comma\n    text = text.slice(0,-2);\n    text += '\\n';\n\n\n    text += '},\\n';\n    text += '\"meta\": {\\n';\n    text += '\\t\"image\": \"'+ g_ctx.tilesetpath+'\",\\n'\n    text += '\\t\"format\": \"RGBA8888\",\\n';\n    text += '\\t\"scale\": \"1\"\\n';\n    text += '}\\n';\n    text += '}\\n';\n\n    //console.log(text);\n    let filename = g_ctx.tilesetpath.split('/').slice(-1)[0];\n    filename = filename.split('.')[0];\n    console.log(\"spritefile: saving to file \",filename);\n    UTIL.download(text, filename+\".json\", \"text/plain\");\n}"
  },
  {
    "path": "src/editor/undo.js",
    "content": "const UNDO_STAX_MAX_LEN = 16\n\nlet undo_stack = [];\nlet undoqueu   = [];\n\nexport function undo_mark_task_start(layer) {\n    undoqueu = [];\n    undoqueu.push(layer);\n}\n\nexport function undo_add_index_to_task(tileindex, oldValue) {\n    undoqueu.push([tileindex, oldValue]);\n}\n\nexport function undo_mark_task_end() {\n    undo_stack.push(undoqueu);\n    if (undo_stack.length > UNDO_STAX_MAX_LEN){\n        undo_stack.shift();\n    }\n}\n\n// utility function for adding a single tile as a task\nexport function undo_add_single_index_as_task(layer, tileindex, oldValue) {\n    undo_mark_task_start(layer);\n    undo_add_index_to_task(tileindex, oldValue);\n    undo_mark_task_end();\n}\n\nexport function undo_pop() {\n    return undo_stack.pop();\n}\n"
  },
  {
    "path": "src/editor/windmill.json",
    "content": "{\"frames\": {\n\n\"pixels_large1.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":0,\"w\":208,\"h\":208},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":208,\"h\":208},\n\t\"sourceSize\": {\"w\":208,\"h\":208}\n},\n\"pixels_large2.png\":\n{\n\t\"frame\": {\"x\":208,\"y\":0,\"w\":208,\"h\":208},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":208,\"h\":208},\n\t\"sourceSize\": {\"w\":208,\"h\":208}\n},\n\"pixels_large3.png\":\n{\n\t\"frame\": {\"x\":416,\"y\":0,\"w\":208,\"h\":208},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":208,\"h\":208},\n\t\"sourceSize\": {\"w\":208,\"h\":208}\n},\n\"pixels_large4.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":208,\"w\":208,\"h\":208},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":208,\"h\":208},\n\t\"sourceSize\": {\"w\":208,\"h\":208}\n},\n\"pixels_large5.png\":\n{\n\t\"frame\": {\"x\":208,\"y\":208,\"w\":208,\"h\":208},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":208,\"h\":208},\n\t\"sourceSize\": {\"w\":208,\"h\":208}\n},\n\"pixels_large6.png\":\n{\n\t\"frame\": {\"x\":416,\"y\":208,\"w\":208,\"h\":208},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":208,\"h\":208},\n\t\"sourceSize\": {\"w\":208,\"h\":208}\n},\n\"pixels_large7.png\":\n{\n\t\"frame\": {\"x\":0,\"y\":416,\"w\":208,\"h\":208},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":208,\"h\":208},\n\t\"sourceSize\": {\"w\":208,\"h\":208}\n},\n\"pixels_large8.png\":\n{\n\t\"frame\": {\"x\":208,\"y\":416,\"w\":208,\"h\":208},\n\t\"rotated\": false,\n\t\"trimmed\": true,\n\t\"spriteSourceSize\": {\"x\":0,\"y\":0,\"w\":208,\"h\":208},\n\t\"sourceSize\": {\"w\":208,\"h\":208}\n}\n},\n\"animations\": {\n\t\"row0\": [\"pixels_large1.png\",\"pixels_large2.png\",\"pixels_large3.png\",\"pixels_large4.png\",\"pixels_large5.png\",\"pixels_large6.png\",\"pixels_large7.png\",\"pixels_large8.png\"]\n},\n\"meta\": {\n\t\"image\": \"./spritesheets/windmill.png\",\n\t\"format\": \"RGBA8888\",\n\t\"size\": {\"w\":624,\"h\":624},\n\t\"scale\": \"1\"\n}\n}\n"
  },
  {
    "path": "src/hooks/sendInput.ts",
    "content": "import { ConvexReactClient, useConvex } from 'convex/react';\nimport { InputArgs, InputReturnValue, Inputs } from '../../convex/aiTown/inputs';\nimport { api } from '../../convex/_generated/api';\nimport { Id } from '../../convex/_generated/dataModel';\n\nexport async function waitForInput(convex: ConvexReactClient, inputId: Id<'inputs'>) {\n  const watch = convex.watchQuery(api.aiTown.main.inputStatus, { inputId });\n  let result = watch.localQueryResult();\n  // The result's undefined if the query's loading and null if the input hasn't\n  // been processed yet.\n  if (result === undefined || result === null) {\n    let dispose: undefined | (() => void);\n    try {\n      await new Promise<void>((resolve, reject) => {\n        dispose = watch.onUpdate(() => {\n          try {\n            result = watch.localQueryResult();\n          } catch (e: any) {\n            reject(e);\n            return;\n          }\n          if (result !== undefined && result !== null) {\n            resolve();\n          }\n        });\n      });\n    } finally {\n      if (dispose) {\n        dispose();\n      }\n    }\n  }\n  if (!result) {\n    throw new Error(`Input ${inputId} was never processed.`);\n  }\n  if (result.kind === 'error') {\n    throw new Error(result.message);\n  }\n  return result.value;\n}\n\nexport function useSendInput<Name extends keyof Inputs>(\n  engineId: Id<'engines'>,\n  name: Name,\n): (args: InputArgs<Name>) => Promise<InputReturnValue<Name>> {\n  const convex = useConvex();\n  return async (args) => {\n    const inputId = await convex.mutation(api.world.sendWorldInput, { engineId, name, args });\n    return await waitForInput(convex, inputId);\n  };\n}\n"
  },
  {
    "path": "src/hooks/serverGame.ts",
    "content": "import { GameId } from '../../convex/aiTown/ids.ts';\nimport { AgentDescription } from '../../convex/aiTown/agentDescription.ts';\nimport { PlayerDescription } from '../../convex/aiTown/playerDescription.ts';\nimport { World } from '../../convex/aiTown/world.ts';\nimport { WorldMap } from '../../convex/aiTown/worldMap.ts';\nimport { Id } from '../../convex/_generated/dataModel';\nimport { useMemo } from 'react';\nimport { useQuery } from 'convex/react';\nimport { api } from '../../convex/_generated/api';\nimport { parseMap } from '../../convex/util/object.ts';\n\nexport type ServerGame = {\n  world: World;\n  playerDescriptions: Map<GameId<'players'>, PlayerDescription>;\n  agentDescriptions: Map<GameId<'agents'>, AgentDescription>;\n  worldMap: WorldMap;\n};\n\n// TODO: This hook reparses the game state (even if we're not rerunning the query)\n// when used in multiple components. Move this to a context to only parse it once.\nexport function useServerGame(worldId: Id<'worlds'> | undefined): ServerGame | undefined {\n  const worldState = useQuery(api.world.worldState, worldId ? { worldId } : 'skip');\n  const descriptions = useQuery(api.world.gameDescriptions, worldId ? { worldId } : 'skip');\n  const game = useMemo(() => {\n    if (!worldState || !descriptions) {\n      return undefined;\n    }\n    return {\n      world: new World(worldState.world),\n      agentDescriptions: parseMap(\n        descriptions.agentDescriptions,\n        AgentDescription,\n        (p) => p.agentId,\n      ),\n      playerDescriptions: parseMap(\n        descriptions.playerDescriptions,\n        PlayerDescription,\n        (p) => p.playerId,\n      ),\n      worldMap: new WorldMap(descriptions.worldMap),\n    };\n  }, [worldState, descriptions]);\n  return game;\n}\n"
  },
  {
    "path": "src/hooks/useHistoricalTime.ts",
    "content": "import { Doc } from '../../convex/_generated/dataModel';\nimport { useEffect, useRef, useState } from 'react';\n\nexport function useHistoricalTime(engineStatus?: Doc<'engines'>) {\n  const timeManager = useRef(new HistoricalTimeManager());\n  const rafRef = useRef<number>();\n  const [historicalTime, setHistoricalTime] = useState<number | undefined>(undefined);\n  if (engineStatus) {\n    timeManager.current.receive(engineStatus);\n  }\n  const updateTime = (performanceNow: number) => {\n    // We don't need sub-millisecond precision for interpolation, so just use `Date.now()`.\n    const now = Date.now();\n    setHistoricalTime(timeManager.current.historicalServerTime(now));\n    rafRef.current = requestAnimationFrame(updateTime);\n  };\n  useEffect(() => {\n    rafRef.current = requestAnimationFrame(updateTime);\n    return () => cancelAnimationFrame(rafRef.current!);\n  }, []);\n  return { historicalTime, timeManager: timeManager.current };\n}\n\ntype ServerTimeInterval = {\n  startTs: number;\n  endTs: number;\n};\n\nexport class HistoricalTimeManager {\n  intervals: Array<ServerTimeInterval> = [];\n  prevClientTs?: number;\n  prevServerTs?: number;\n  totalDuration: number = 0;\n\n  latestEngineStatus?: Doc<'engines'>;\n\n  receive(engineStatus: Doc<'engines'>) {\n    this.latestEngineStatus = engineStatus;\n    if (!engineStatus.currentTime || !engineStatus.lastStepTs) {\n      return;\n    }\n    const latest = this.intervals[this.intervals.length - 1];\n    if (latest) {\n      if (latest.endTs === engineStatus.currentTime) {\n        return;\n      }\n      if (latest.endTs > engineStatus.currentTime) {\n        throw new Error(`Received out-of-order engine status`);\n      }\n    }\n    const newInterval = {\n      startTs: engineStatus.lastStepTs,\n      endTs: engineStatus.currentTime,\n    };\n    this.intervals.push(newInterval);\n    this.totalDuration += newInterval.endTs - newInterval.startTs;\n  }\n\n  historicalServerTime(clientNow: number): number | undefined {\n    if (this.intervals.length == 0) {\n      return undefined;\n    }\n    if (clientNow === this.prevClientTs) {\n      return this.prevServerTs;\n    }\n    // If this is our first time simulating, start at the beginning of the buffer.\n    const prevClientTs = this.prevClientTs ?? clientNow;\n    const prevServerTs = this.prevServerTs ?? this.intervals[0].startTs;\n    const lastServerTs = this.intervals[this.intervals.length - 1].endTs;\n\n    // Simple rate adjustment: run time at 1.2 speed if we're more than 1s behind and\n    // 0.8 speed if we only have 100ms of buffer left. A more sophisticated approach\n    // would be to continuously adjust the rate based on the size of the buffer.\n    const bufferDuration = lastServerTs - prevServerTs;\n    let rate = 1;\n    if (bufferDuration < SOFT_MIN_SERVER_BUFFER_AGE) {\n      rate = 0.8;\n    } else if (bufferDuration > SOFT_MAX_SERVER_BUFFER_AGE) {\n      rate = 1.2;\n    }\n    let serverTs = Math.max(\n      prevServerTs + (clientNow - prevClientTs) * rate,\n      // Jump forward if we're too far behind.\n      lastServerTs - MAX_SERVER_BUFFER_AGE,\n    );\n\n    let chosen = null;\n    for (let i = 0; i < this.intervals.length; i++) {\n      const snapshot = this.intervals[i];\n      // We're past this snapshot, continue to the next one.\n      if (snapshot.endTs < serverTs) {\n        continue;\n      }\n      // We're cleanly within this snapshot.\n      if (serverTs >= snapshot.startTs) {\n        chosen = i;\n        break;\n      }\n      // We've gone past the desired timestamp, which implies a gap in our server state.\n      // Jump time forward to the beginning of this snapshot.\n      if (serverTs < snapshot.startTs) {\n        serverTs = snapshot.startTs;\n        chosen = i;\n      }\n    }\n    if (chosen === null) {\n      serverTs = this.intervals.at(-1)!.endTs;\n      chosen = this.intervals.length - 1;\n    }\n    // Time only moves forward, so we can trim all of the snapshots before our chosen one.\n    const toTrim = Math.max(chosen - 1, 0);\n    if (toTrim > 0) {\n      for (const snapshot of this.intervals.slice(0, toTrim)) {\n        this.totalDuration -= snapshot.endTs - snapshot.startTs;\n      }\n      this.intervals = this.intervals.slice(toTrim);\n    }\n\n    this.prevClientTs = clientNow;\n    this.prevServerTs = serverTs;\n\n    return serverTs;\n  }\n\n  bufferHealth(): number {\n    if (!this.intervals.length) {\n      return 0;\n    }\n    const lastServerTs = this.prevServerTs ?? this.intervals[0].startTs;\n    return this.intervals[this.intervals.length - 1].endTs - lastServerTs;\n  }\n\n  clockSkew(): number {\n    if (!this.prevClientTs || !this.prevServerTs) {\n      return 0;\n    }\n    return this.prevClientTs - this.prevServerTs;\n  }\n}\n\nconst MAX_SERVER_BUFFER_AGE = 1500;\nconst SOFT_MAX_SERVER_BUFFER_AGE = 1250;\nconst SOFT_MIN_SERVER_BUFFER_AGE = 250;\n"
  },
  {
    "path": "src/hooks/useHistoricalValue.ts",
    "content": "import { FieldConfig, History, unpackSampleRecord } from '../../convex/engine/historicalObject';\nimport { useMemo, useRef } from 'react';\n\nexport function useHistoricalValue<T extends Record<string, number>>(\n  fields: FieldConfig,\n  historicalTime: number | undefined,\n  value: T | undefined,\n  history: ArrayBuffer | undefined,\n): T | undefined {\n  const manager = useRef(new HistoryManager());\n  const sampleRecord: Record<string, History> | undefined = useMemo(() => {\n    if (!value || !history) {\n      return undefined;\n    }\n    if (!(history instanceof ArrayBuffer)) {\n      throw new Error(`Expected ArrayBuffer, found ${typeof history}`);\n    }\n    return unpackSampleRecord(fields, history);\n  }, [value && history]);\n  if (sampleRecord) {\n    manager.current.receive(sampleRecord);\n  }\n  if (value === undefined) {\n    return undefined;\n  }\n  if (!historicalTime) {\n    return value;\n  }\n  const historicalFields = manager.current.query(historicalTime);\n  return { ...value, ...historicalFields };\n}\n\nclass HistoryManager {\n  histories: Record<string, History[]> = {};\n\n  receive(sampleRecord: Record<string, History>) {\n    for (const [fieldName, history] of Object.entries(sampleRecord)) {\n      let histories = this.histories[fieldName];\n      if (!histories) {\n        histories = [];\n        this.histories[fieldName] = histories;\n      }\n      if (histories[histories.length - 1] == history) {\n        continue;\n      }\n      histories.push(history);\n    }\n  }\n\n  query(historicalTime: number): Record<string, number> {\n    const result: Record<string, number> = {};\n    for (const [fieldName, histories] of Object.entries(this.histories)) {\n      if (histories.length == 0) {\n        continue;\n      }\n      let foundIndex = null;\n      let currentValue = histories[0].initialValue;\n      for (let i = 0; i < histories.length; i++) {\n        const history = histories[i];\n        for (const sample of history.samples) {\n          if (sample.time > historicalTime) {\n            foundIndex = i;\n            break;\n          }\n          currentValue = sample.value;\n        }\n        if (foundIndex !== null) {\n          break;\n        }\n      }\n      if (foundIndex !== null) {\n        this.histories[fieldName] = histories.slice(foundIndex);\n      }\n      result[fieldName] = currentValue;\n    }\n    return result;\n  }\n}\n"
  },
  {
    "path": "src/hooks/useWorldHeartbeat.ts",
    "content": "import { useMutation, useQuery } from 'convex/react';\nimport { useEffect } from 'react';\nimport { api } from '../../convex/_generated/api';\nimport { WORLD_HEARTBEAT_INTERVAL } from '../../convex/constants';\n\nexport function useWorldHeartbeat() {\n  const worldStatus = useQuery(api.world.defaultWorldStatus);\n  const worldId = worldStatus?.worldId;\n\n  // Send a periodic heartbeat to our world to keep it alive.\n  const heartbeat = useMutation(api.world.heartbeatWorld);\n  useEffect(() => {\n    const sendHeartBeat = () => {\n      if (!worldStatus) {\n        return;\n      }\n      // Don't send a heartbeat if we've observed one sufficiently close\n      // to the present.\n      if (Date.now() - WORLD_HEARTBEAT_INTERVAL / 2 < worldStatus.lastViewed) {\n        return;\n      }\n      void heartbeat({ worldId: worldStatus.worldId });\n    };\n    sendHeartBeat();\n    const id = setInterval(sendHeartBeat, WORLD_HEARTBEAT_INTERVAL);\n    return () => clearInterval(id);\n    // Rerun if the `worldId` changes but not `worldStatus`, since don't want to\n    // resend the heartbeat whenever its last viewed timestamp changes.\n  }, [worldId, heartbeat]);\n}\n"
  },
  {
    "path": "src/index.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@font-face {\n  font-family: 'Upheaval Pro';\n  src: url(/assets/fonts/upheaval_pro.ttf);\n}\n\n@font-face {\n  font-family: 'VCR OSD Mono';\n  src: url(/assets/fonts/vcr_osd_mono.ttf);\n}\n\n.font-display {\n  font-family: 'Upheaval Pro', 'sans-serif';\n}\n\n.font-body {\n  font-family: 'VCR OSD Mono', 'monospace';\n}\n\n.font-system {\n  font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n}\n\n:root {\n  --foreground-rgb: 0, 0, 0;\n  --background-start-rgb: 214, 219, 220;\n  --background-end-rgb: 255, 255, 255;\n}\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    --foreground-rgb: 255, 255, 255;\n    --background-start-rgb: 0, 0, 0;\n    --background-end-rgb: 0, 0, 0;\n  }\n}\n\nbody {\n  color: rgb(var(--foreground-rgb));\n  background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb)))\n    rgb(var(--background-start-rgb));\n}\n\n.game-background {\n  background: linear-gradient(rgba(41, 41, 41, 0.8), rgba(41, 41, 41, 0.8)),\n    url(../assets/background.webp);\n  background-blend-mode: hard-light;\n  background-position: center;\n  background-repeat: no-repeat;\n  background-size: cover;\n  background-attachment: fixed;\n}\n\n.game-title {\n  background: linear-gradient(to bottom, #fec742, #dd7c42);\n  background-clip: text;\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n  filter: drop-shadow(0px 0.08em 0px #6e2146);\n}\n\n.game-frame {\n  border-width: 36px;\n  border-image-source: url(../assets/ui/frame.svg);\n  border-image-repeat: stretch;\n  border-image-slice: 25%;\n}\n\n.game-progress-bar {\n  border: 5px solid rgb(23, 20, 33);\n}\n\n@keyframes moveStripes {\n  to {\n    background-position: calc(100% + 28px) 0;\n  }\n}\n\n.game-progress-bar-progress {\n  background: repeating-linear-gradient(135deg, white, white 10px, #dfdfdf 10px, #dfdfdf 20px);\n  background-size: 200% 100%;\n  background-position: 100% 0;\n  animation: moveStripes 0.5s linear infinite;\n}\n\n@media screen and (min-width: 640px) {\n  .game-frame {\n    border-width: 48px;\n  }\n}\n\n.shadow-solid {\n  text-shadow: 0 0.1em 0 rgba(0, 0, 0, 0.5);\n}\n\n.bubble {\n  border-width: 30px;\n  border-image-source: url(../assets/ui/bubble-left.svg);\n  border-image-repeat: stretch;\n  border-image-slice: 20%;\n}\n\n.bubble-mine {\n  border-image-source: url(../assets/ui/bubble-right.svg);\n}\n\n.box {\n  border-width: 12px;\n  border-image-source: url(../assets/ui/box.svg);\n  border-image-repeat: stretch;\n  border-image-slice: 12.5%;\n}\n\n.desc {\n  border-width: 56px;\n  border-image-source: url(../assets/ui/desc.svg);\n  border-image-repeat: stretch;\n  border-image-slice: 28%;\n}\n\n.chats {\n  border-width: 24px;\n  border-image-source: url(../assets/ui/chats.svg);\n  border-image-repeat: stretch;\n  border-image-slice: 40%;\n}\n\n.login-prompt {\n  border-width: 48px;\n  border-image-source: url(../assets/ui/jewel_box.svg);\n  border-image-repeat: stretch;\n  border-image-slice: 40%;\n}\n\n.button {\n  border-width: 1em;\n  border-image-source: url(../assets/ui/button.svg);\n  border-image-repeat: stretch;\n  border-image-slice: 25%;\n  cursor: pointer;\n}\n\n.button span {\n  display: inline-block;\n  transform: translateY(-15%);\n}\n\n@media (max-width: 640px) {\n  .button {\n    height: 40px;\n    border-width: 0.75em;\n    font-size: 16px;\n  }\n\n  .button > div,\n  .button > span {\n    vertical-align: top;\n    line-height: 1;\n  }\n}\n\n.button:hover {\n  opacity: 0.8;\n}\n\n.button:active {\n  /* Inlining this image to avoid flashing during loading */\n  border-image-source: url(\"data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1' y='2' width='14' height='13' fill='%23181425'/%3E%3Crect x='2' y='1' width='12' height='15' fill='%23181425'/%3E%3Crect y='3' width='16' height='11' fill='%23181425'/%3E%3Crect x='2' y='14' width='12' height='1' fill='%23262B44'/%3E%3Crect x='1' y='3' width='14' height='11' fill='%233A4466'/%3E%3Crect x='2' y='2' width='12' height='9' fill='%233A4466'/%3E%3Crect x='1' y='13' width='1' height='1' fill='%23262B44'/%3E%3Crect x='14' y='13' width='1' height='1' fill='%23262B44'/%3E%3C/svg%3E%0A\");\n}\n\n.button:active span {\n  transform: none;\n}\n\np[contenteditable='true']:empty::before {\n  content: attr(placeholder);\n  color: #aaa;\n}\n\n.shape-top-left-corner {\n  clip-path: polygon(0 0, 100% 0, 0 100%);\n}\n"
  },
  {
    "path": "src/main.tsx",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport Home from './App.tsx';\nimport './index.css';\nimport 'uplot/dist/uPlot.min.css';\nimport 'react-toastify/dist/ReactToastify.css';\nimport ConvexClientProvider from './components/ConvexClientProvider.tsx';\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <React.StrictMode>\n    <ConvexClientProvider>\n      <Home />\n    </ConvexClientProvider>\n  </React.StrictMode>,\n);\n"
  },
  {
    "path": "src/toasts.ts",
    "content": "import { toast } from 'react-toastify';\n\nexport async function toastOnError<T>(promise: Promise<T>): Promise<T> {\n  try {\n    return await promise;\n  } catch (error: any) {\n    toast.error(error.message);\n    throw error;\n  }\n}\n"
  },
  {
    "path": "src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],\n  theme: {\n    fontFamily: {\n      display: ['var(--font-display)', 'sans-serif'],\n      body: ['var(--font-body)', 'monospace'],\n    },\n    extend: {\n      colors: {\n        brown: {\n          100: '#FFFFFF',\n          200: '#EAD4AA',\n          300: '#E4A672',\n          500: '#B86F50',\n          700: '#743F39',\n          800: '#3F2832',\n          900: '#181425',\n        },\n        clay: {\n          100: '#C0CBDC',\n          300: '#8B9BB4',\n          500: '#5A6988',\n          700: '#3A4466',\n          900: '#181425',\n        },\n      },\n    },\n  },\n  plugins: [require('@tailwindcss/forms')],\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2015\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"allowImportingTsExtensions\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  },\n  \"include\": [\"**/*.ts\", \"**/*.tsx\", \"**/*.js\", \"postcss.config.js\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "vercel.json",
    "content": "{\n  \"framework\": \"vite\",\n  \"rewrites\": [\n    {\n      \"source\": \"/ai-town/:match*\",\n      \"destination\": \"/:match*\"\n    }\n  ]\n}\n"
  },
  {
    "path": "vite.config.ts",
    "content": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  base: '/ai-town',\n  plugins: [react()],\n  server: {\n    allowedHosts: ['ai-town-your-app-name.fly.dev', 'localhost', '127.0.0.1'],\n  },\n});\n"
  }
]