Repository: a16z-infra/ai-town Branch: main Commit: 2693ed6973e3 Files: 159 Total size: 1.0 MB Directory structure: gitextract_ov7oqzfx/ ├── .dockerignore ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── .vercelignore ├── .vscode/ │ ├── convex.code-snippets │ └── settings.json ├── ARCHITECTURE.md ├── Dockerfile ├── LICENSE ├── README.md ├── convex/ │ ├── _generated/ │ │ ├── api.d.ts │ │ ├── api.js │ │ ├── dataModel.d.ts │ │ ├── server.d.ts │ │ └── server.js │ ├── agent/ │ │ ├── conversation.ts │ │ ├── embeddingsCache.ts │ │ ├── memory.ts │ │ └── schema.ts │ ├── aiTown/ │ │ ├── agent.ts │ │ ├── agentDescription.ts │ │ ├── agentInputs.ts │ │ ├── agentOperations.ts │ │ ├── conversation.ts │ │ ├── conversationMembership.ts │ │ ├── game.ts │ │ ├── ids.ts │ │ ├── inputHandler.ts │ │ ├── inputs.ts │ │ ├── insertInput.ts │ │ ├── location.ts │ │ ├── main.ts │ │ ├── movement.ts │ │ ├── player.ts │ │ ├── playerDescription.ts │ │ ├── schema.ts │ │ ├── world.ts │ │ └── worldMap.ts │ ├── constants.ts │ ├── crons.ts │ ├── engine/ │ │ ├── abstractGame.ts │ │ ├── historicalObject.test.ts │ │ ├── historicalObject.ts │ │ └── schema.ts │ ├── http.ts │ ├── init.ts │ ├── messages.ts │ ├── music.ts │ ├── schema.ts │ ├── testing.ts │ ├── util/ │ │ ├── FastIntegerCompression.ts │ │ ├── assertNever.ts │ │ ├── asyncMap.test.ts │ │ ├── asyncMap.ts │ │ ├── compression.test.ts │ │ ├── compression.ts │ │ ├── geometry.test.ts │ │ ├── geometry.ts │ │ ├── isSimpleObject.ts │ │ ├── llm.ts │ │ ├── minheap.test.ts │ │ ├── minheap.ts │ │ ├── object.ts │ │ ├── sleep.ts │ │ ├── types.test.ts │ │ ├── types.ts │ │ └── xxhash.ts │ └── world.ts ├── data/ │ ├── animations/ │ │ ├── campfire.json │ │ ├── gentlesparkle.json │ │ ├── gentlesplash.json │ │ ├── gentlewaterfall.json │ │ └── windmill.json │ ├── characters.ts │ ├── convertMap.js │ ├── gentle.js │ └── spritesheets/ │ ├── f1.ts │ ├── f2.ts │ ├── f3.ts │ ├── f4.ts │ ├── f5.ts │ ├── f6.ts │ ├── f7.ts │ ├── f8.ts │ ├── p1.ts │ ├── p2.ts │ ├── p3.ts │ ├── player.ts │ └── types.ts ├── docker-compose.yml ├── fly/ │ ├── README.md │ ├── backend/ │ │ └── fly.toml │ └── dashboard/ │ └── fly.toml ├── index.html ├── jest.config.ts ├── package.json ├── postcss.config.js ├── public/ │ └── assets/ │ └── tilemap.json ├── src/ │ ├── App.tsx │ ├── components/ │ │ ├── Character.tsx │ │ ├── ConvexClientProvider.tsx │ │ ├── DebugPath.tsx │ │ ├── DebugTimeManager.tsx │ │ ├── FreezeButton.tsx │ │ ├── Game.tsx │ │ ├── MessageInput.tsx │ │ ├── Messages.tsx │ │ ├── PixiGame.tsx │ │ ├── PixiStaticMap.tsx │ │ ├── PixiViewport.tsx │ │ ├── Player.tsx │ │ ├── PlayerDetails.tsx │ │ ├── PositionIndicator.tsx │ │ ├── PoweredByConvex.tsx │ │ └── buttons/ │ │ ├── Button.tsx │ │ ├── InteractButton.tsx │ │ ├── LoginButton.tsx │ │ └── MusicButton.tsx │ ├── editor/ │ │ ├── README.md │ │ ├── campfire.json │ │ ├── eutils.js │ │ ├── gentlesparkle.json │ │ ├── gentlesplash.json │ │ ├── gentlewaterfall.json │ │ ├── index.html │ │ ├── le.html │ │ ├── le.js │ │ ├── leconfig.js │ │ ├── lecontext.js │ │ ├── lehtmlui.js │ │ ├── mapfile.js │ │ ├── maps/ │ │ │ ├── gentle-full.js │ │ │ ├── gentle.js │ │ │ ├── gentleanim.js │ │ │ ├── mage3.js │ │ │ └── serene.js │ │ ├── se.html │ │ ├── se.js │ │ ├── seconfig.js │ │ ├── secontext.js │ │ ├── sehtmlui.js │ │ ├── spritefile.js │ │ ├── undo.js │ │ └── windmill.json │ ├── hooks/ │ │ ├── sendInput.ts │ │ ├── serverGame.ts │ │ ├── useHistoricalTime.ts │ │ ├── useHistoricalValue.ts │ │ └── useWorldHeartbeat.ts │ ├── index.css │ ├── main.tsx │ ├── toasts.ts │ └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── vercel.json └── vite.config.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ # flyctl launch added from .gitignore # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies node_modules **/game/node_modules .pnp **/.pnp.js # testing coverage # next.js .next out # production build # misc **/.DS_Store **/*.pem # debug **/npm-debug.log* **/yarn-debug.log* **/yarn-error.log* # local env files **/.env*.local # vercel **/.vercel # typescript **/*.tsbuildinfo **/next-env.d.ts **/.env .env.prod fly.toml # Vite build **/dist **/convex-local* **/convex_local* ================================================ FILE: .eslintignore ================================================ webpack* .eslintrc.js next.config.js tailwind.config.js postcss.config.js convex/_generated/* dist/* ================================================ FILE: .eslintrc.js ================================================ export default { parser: '@typescript-eslint/parser', // Specifies the ESLint parser plugins: ['@typescript-eslint'], extends: [ 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 'plugin:@typescript-eslint/recommended-type-checked', ], parserOptions: { project: './tsconfig.json', ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features sourceType: 'module', // Allows for the use of imports }, rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/no-unused-vars': [ 'warn', { varsIgnorePattern: '^_', argsIgnorePattern: '^_' }, ], '@typescript-eslint/no-non-null-assertion': 'off', }, }; ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules game/node_modules/ /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env*.local # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts .env /.env.prod # Vite build dist convex-local* convex_local* ================================================ FILE: .prettierrc ================================================ { "trailingComma": "all", "singleQuote": true, "bracketSpacing": true, "tabWidth": 2, "proseWrap": "always", "printWidth": 100 } ================================================ FILE: .vercelignore ================================================ convex-local* convex_local* ================================================ FILE: .vscode/convex.code-snippets ================================================ { "Convex Imports": { "prefix": "convex:imports", "body": [ "import { v } from \"convex/values\";", "import { api, internal } from \"./_generated/api\";", "import { Doc, Id } from \"./_generated/dataModel\";", "import {", " action,", " internalAction,", " internalMutation,", " internalQuery,", " mutation,", " query,", "} from \"./_generated/server\";" ], "scope": "javascript,typescript", "isFileTemplate": true }, "Convex Query": { "prefix": "convex:query", "body": [ "export const $1 = query({", " args: {},", " handler: async (ctx, args) => {", " $0", " },", "});" ], "scope": "javascript,typescript" }, "Convex Internal Query": { "prefix": "convex:internalQuery", "body": [ "export const $1 = internalQuery({", " args: {},", " handler: async (ctx, args) => {", " $0", " },", "});" ], "scope": "javascript,typescript" }, "Convex Mutation": { "prefix": "convex:mutation", "body": [ "export const $1 = mutation({", " args: {},", " handler: async (ctx, args) => {", " $0", " },", "});" ], "scope": "javascript,typescript" }, "Convex Internal Mutation": { "prefix": "convex:internalMutation", "body": [ "export const $1 = internalMutation({", " args: {},", " handler: async (ctx, args) => {", " $0", " },", "});" ], "scope": "javascript,typescript" }, "Convex Action": { "prefix": "convex:action", "body": [ "import { action } from \"./_generated/server\";", "", "export const $1 = action({", " args: {},", " handler: async (ctx, args) => {", " $0", " },", "});" ], "scope": "javascript,typescript" }, "Convex Internal Action": { "prefix": "convex:internalAction", "body": [ "import { internalAction } from \"./_generated/server\";", "", "export const $1 = internalAction({", " args: {},", " handler: async (ctx, args) => {", " $0", " },", "});" ], "scope": "javascript,typescript" } } ================================================ FILE: .vscode/settings.json ================================================ { "editor.formatOnSave": true, "editor.tabSize": 2, "[html]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[jsonc]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "typescript.preferences.importModuleSpecifierEnding": "auto", "javascript.preferences.importModuleSpecifierEnding": "auto" } ================================================ FILE: ARCHITECTURE.md ================================================ # Architecture This documents dives into the high-level architecture of AI Town and its different layers. We'll first start with a brief overview and then go in-depth on each component. The overview should be sufficient for forking AI Town and changing game or agent behavior. Read on to the deep dives if you're interested or running up against the engine's limitations. This doc assumes the reader has a working knowledge of Convex. If you're new to Convex, check out the [Convex tutorial](https://docs.convex.dev/get-started) to get started. ## Overview AI Town is split into a few layers: - The server-side game logic in `convex/aiTown`: This layer defines what state AI Town maintains, how it evolves over time, and how it reacts to user input. Both humans and agents submit inputs that the game engine processes. - The client-side game UI in `src/`: AI Town uses `pixi-react` to render the game state to the browser for human consumption. - The game engine in `convex/engine`: To make it easy to hack on the game rules, we've separated out the game engine from the AI Town-specific game rules. The game engine is responsible for saving and loading game state from the database, coordinating feeding inputs into the engine, and actually running the game engine in Convex functions. - The agent in `convex/agent`: Agents run as part of the game loop, and can kick off asynchronous Convex functions to do longer processing, such as talking to LLMs. Those functions can save state in separate tables, or submit inputs to the game engine to modify game state. Internally, our agents use a combination of simple rule-based systems and talking to an LLM. So, if you'd like to tweak agent behavior but keep the same game mechanics, check out `convex/agent` for the async work, and `convex/aiTown/agent.ts` for the game loop logic. If you would like to add new gameplay elements (that both humans and agents can interact with), add the feature to `convex/aiTown`, render it in the UI in `src/`, and respond to it in `convex/aiTown/agent.ts`. If you have parts of your game that are more latency sensitive, you can move them out of engine into regular Convex tables, queries, and mutations, only logging key bits into game state. See "Message data model" below for an example. ## AI Town game logic (`convex/aiTown`) ### Data model AI Town's data model has a few concepts: - Worlds (`convex/aiTown/world.ts`) represent a map with many players interacting together. - Players (`convex/aiTown/player.ts`) are the core characters in the game. Players have human readable names and descriptions, and they may be associated with a human user. At any point in time, a player may be pathfinding towards some destination and has a current location. - Conversations (`convex/aiTown/conversations.ts`) are created by a player and end at some point in time. - Conversation memberships (`convex/aiTown/conversationMembership.ts`) indicate that a player is a member of a conversation. Players may only be in one conversation at any point in time, and conversations currently have exactly two members. Memberships may be in one of three states: - `invited`: The player has been invited to the conversation but hasn't accepted yet. - `walkingOver`: The player has accepted the invite to the conversation but is too far away to talk. The player will automatically join the conversation when they get close enough. - `participating`: The player is actively participating in the conversation. ### Schema There are three main categories of tables: 1. Engine tables (`convex/engine/schema.ts`) for maintaining engine-internal state. 2. Game tables (`convex/aiTown/schema.ts`) for game state. To keep game state small and efficient to read and write, we store AI Town's data model across a few tables. See `convex/aiTown/schema.ts` for an overview. 3. Agent tables (`convex/agent/schema.ts`) for agent state. Agents can freely read and write to these tables within their actions. ### Inputs (`convex/aiTown/inputs.ts`) AI Town modifies its data model by processing inputs. Inputs are submitted by players and agents and processed by the game engine. We specify inputs in the `inputs` object in `convex/aiTown/inputs.ts`. Use the `inputHandler` function to construct an input handler, specifying a Convex validator for arguments for end-to-end type-safety. - Joining (`join`) and leaving (`leave`) the game. - Moving a player to a particular location (`moveTo`): Movement in AI Town is similar to RTS games, where the players specify where they want to go, and the engine figures out how to get there. - Starting a conversation (`startConversation`), accepting an invite (`acceptInvite`), rejecting an invite (`rejectInvite`), and leaving a conversation (`leaveConversation`). To track typing indicators, you use `startTyping` and `finishSendingMessage`. These are imported from `game/conversations.ts`. - Agent inputs are imported from `aiTown/agentInputs.ts` for things like remembering conversations, deciding what to do, etc. Each of these inputs' implementation method checks invariants and updates game state as desired. For example, the `moveTo` input checks that the player isn't participating in a conversation, throwing an error telling them to leave the conversation first if so, and then updates their pathfinding state with the desired destination. ### Simulation Other than when processing player inputs, the game state can change over time in the background as the simulation runs time forward. For example, if a player has decided to move along a path, their position will gradually update as time moves forward. Similarly, if two players collide into each other, they'll notice and replan their paths, trying to avoid obstacles. ### Message data model We manage the tables for tracking chat messages in separate tables not affiliated with the game engine. This is for a few reasons: - The core simulation doesn't need to know about messages, so keeping them out keeps game state small. - Messages are updated very frequently (when streamed out from OpenAI) and benefit from lower input latency, so they're not a great fit for the engine. See "Design goals and limitations" below. Messages (`convex/schema.ts`) are in a conversation and indicate an author and message text. Each conversation has a typing state in the conversations table that indicates that a player is currently typing. Players can still send messages while another player is typing, but having the indicator helps agents (and humans) not talk over each other. The separate tables are queried and modified with regular Convex queries and mutations that don't directly go through the simulation. ## Game engine (`convex/engine`) Given the description of AI Town's game behavior in the previous section, the `AbstractGame` class in `convex/engine/abstractGame.ts` implements actually running the simulation. The game engine has a few responsibilities: - Coordinating incoming player inputs, feeding them into the simulation, and sending their return values (or errors) to the client. - Running the simulation forward in time. - Saving and loading game state from the database. - Managing executing the game behavior, efficiently using Convex resources and minimizing input latency. AI Town's game behavior is implemented in the `Game` subclass. ### Input handling Users submit inputs through the `insertInput` function, which inserts them into an `inputs` table, assigning a monotonically increasing unique input number and stamping the input with the time the server received it. The engine then processes inputs, writing their results back to the `inputs` row. Interested clients can subscribe on an input's status with the `inputStatus` query. `Game` provides an abstract method `handleInput` that `AiTown` implements with its specific behavior. ### Running the simulation The `Game` class specifies how it simulates time forward with the `tick` method: - `tick(now)` runs the simulation forward until the given timestamp - Ticks are run at a high frequency, configurable with `tickDuration` (milliseconds). Since AI town has smooth motion for player movement, it runs at 60 ticks per second. - It's generally a good idea to break up game logic into separate systems that can be ticked forward independently. For example, AI Town's `tick` method advances pathfinding with `Player.tickPathfinding`, player positions with `Player.tickPosition`, conversations with `Conversation.tick`, and `Agent.tick` for agent logic. To avoid running a Convex mutation 60 times per second (which would be expensive and slow), the engine batches up many ticks into a _step_. AI town runs steps at only 1 time per second. Here's how a step works: 1. Load the game state into memory. 2. Decide how long to run. 3. Execute many ticks for our time interval, alternating between feeding in inputs with `handleInput` and advancing the simulation with `tick`. 4. Write the updated game state back to the database. One core invariant is that the game engine is fully "single-threaded" per world, so there are never two runs of an engine's step overlapping in time. Not having to think about race conditions or concurrency makes writing game engine code a lot easier. However, preserving this invariant is a little tricky. If the engine is idle for a minute and an input comes in, we want to run the engine immediately but then cancel its run after the minute's up. If we're not careful, a race condition may cause us to run multiple copies of the engine if an input comes in just as an idle timeout is expiring! Our approach is to store a generation number with the engine that monotonically increases over time. All scheduled runs of the engine contain their expected generation number as an argument. Then, if we'd like to cancel a future run of the engine, we can bump the generation number by one, and then we're guaranteed that the subsequent run will fail immediately as it'll notice that the engine's generation number does not match its expected one. ### Engine state management The `World`, `Player`, `Conversation`, and `Agent` classes coordinate loading data into memory from the database, modifying it according to the game rules, and serializing it to write back out to the database. Here's the flow: 1. The Convex scheduler calls the `convex/aiTown/main.ts:runStep` action. 2. The `runStep` action calls `convex/aiTown/game.ts:loadWorld` to load the current game state. This query calls `Game.load`, which loads all of a world's game state from the appropriate tables, and returns a `GameState` object, which contains serialized versions of all of the players, agents, etc. 3. The `runStep` action passes the `GameState` to the `Game` constructor, which parses the serialized versions of all our game objects using their constructors. For example, `new Player(serializedPlayer)` parses the database representation into the in-memory `Player` class. 4. The engine runs the simulation, modifying the in-memory game objects. 5. At the end of a step, the framework calls `Game.saveStep`, which computes a diff of the game state since the beginning of the step and passes the diff to the `convex/aiTown/game.ts:saveWorld` mutation. 6. The `saveWorld` mutation applies the diff to the database, notices if any deleted objects need to be archived, updates the `participatedTogether` graph, and kicks off any scheduled jobs to run. 7. Since the engine is the only mutator of game state, it continues to run steps for some amount of time without repeating steps 1 to 3 again. Just as we assume that the game engine is "single threaded", we also assume that the game engine _exclusively_ owns the tables that store game engine state. Only the game engine should programmatically modify these tables, so components outside the engine can only mutate them by sending inputs. ### Historical tables If we're only writing updates out to the database at the end of the step, and steps are only running at once per second, continuous quantities like position will only update every second. This, then, defeats the whole purpose of having high-frequency ticks: Player positions will jump around and look choppy. To solve this, we track the historical values of quantities like position _within_ a step, storing the value at the end of each tick. Then, the client receives both the current value _and_ the past step's worth of history, and it can "replay" the history to make the motion smooth. The game tracks these quantities at the end of each tick by feeding them to a `HistoricalObject`. This object efficiently tracks its changes over time and serializes them into a buffer that clients can use for replaying its history. There are a few limitations on `HistoricalObject`: - Historical objects can only have numeric (floating point) values and can't have nested objects or optional fields. - Historical objects must declare which fields they'd like to track. We store each player's "location" (i.e. its position, orientation, and speed) in a `HistoricalObject` and write it to the `worlds` document at the end of a step when computing a diff. ## Client-side game UI (`src/`) One guiding principle for AI Town's architecture is to keep the usage as close to "regular Convex" usage as possible. So, game state is stored in regular tables, and the UI just uses regular `useQuery` hooks to load that state and render it in the UI. The one exception is for historical tables, which feed in the latest state into a `useHistoricalValue` hook that parses the history buffer and replays time forward for smooth motion. To keep replayed time synchronized across multiple historical buffers, we provide a `useHistoricalTime` hook for the top of your app that keeps track of the current time and returns it for you to pass down into components. We also provide a `useSendInput` hook that wraps `useMutation` and automatically sends inputs to the server and waits for the engine to process them and return their outcome. ## Agent architecture (`convex/agent`) ### The agent loop (`convex/game/agents.ts`) Agents will execute any game state changes, and schedule operations to do anything that requires a long-lived request or accessing non-game tables. The flow generally is: 1. Logic in `Agent.tick` can read and modify game state as time progresses, such as waiting until the agent is near another player to start talking. 2. When there is something that needs to talk to an LLM or read/write external data, it calls `startOperation` with a reference to a Convex function: generally an `internalAction`. 3. This function can read state from game tables and other tables via `internalQuery` functions. 4. It executes long-running tasks, and can write data via `internalMutation`s. Game state should not be written, but rather submitted via `inputs` (described in a previous section). 5. Inputs are submitted from actions with `ctx.runMutation(api.game.main.sendInput, {...})` from actions or via `insertInput` from mutations. They are referenced by their name as a string, like `moveTo`. 6. Inputs are defined with `inputHandler` and are given an instance of the AiTown game to modify, similar to the game loop. In fact, these are called as part of the game loop before `tickAgent`. 7. When an operation is done, it deletes the `inProgressOperation`. This is to ensure an agent only is trying to do one thing at a time. 8. `Agent.tick` then can observe the new game state and continue to make decisions. ### Conversations (`convex/agent/conversations.ts`) The agent code calls into the conversation layer which implements the prompt engineering for injecting personality and memories into the GPT responses. It has functions for starting a conversation (`startConversation`), continuing after the first message (`continueConversation`), and politely leaving a conversation (`leaveConversation`). Each function loads structured data from the database, queries the memory layer for the agent's opinion about the player they're talking with, and then calls into the OpenAI client (`convex/util/openai.ts`). ### Memories (`convex/agent/memory.ts`) After each conversation, GPT summarizes its message history, and we compute an embedding of the summary text and write it into Convex's vector database. Then, when starting a new conversation with, Danny, we embed "What you think about Danny?", find the three most similar memories, and fetch their summary texts to inject into the conversation prompt. ### Embeddings cache (`convex/agent/embeddingsCache.ts`) To avoid computing the same embedding over and over again, we cache embeddings by a hash of their text in a Convex table. ## Design goals and limitations AI Town's game engine has a few design goals: - Try to be as close to a regular Convex app as possible. Use regular client hooks (like `useQuery`) when possible, and store game state in regular tables. - Be as similar to existing engines as possible, so it's easy to change the behavior. We chose a `tick()` based model for simulation since it's commonly used elsewhere and intuitive. - Decouple agent behavior from the game engine. It's nice to allow human players and AI agents to do all the same things in the game. These design goals imply some inherent limitations: - All data is loaded into memory each step. The active game state loaded by the game should be small enough to fit into memory and load and save frequently. Try to keep game state to less than a few dozen kilobytes: Games that require tens of thousands of objects interacting together may not be a good fit. - All inputs are fed through the database in the `inputs` table, so applications that require very large or frequent inputs may not be a good fit. - Input latency will be around one RTT (time for the input to make it to the server and the response to come back) plus half the step size (for expected server input delay when the input's waiting for the next step). Historical values add another half step size of input latency since their values are viewed slightly in the past. As configured, this will roughly be around 1.5s of input latency, which won't be a good fit for competitive games. You can configure the step size to be smaller (e.g. 250ms) which will decrease input latency at the cost of adding more Convex function calls and database bandwidth. - The game engine is designed to be single threaded. JavaScript operating over plain objects in-memory can be surprisingly fast, but if your simulation is very computationally expensive, it may not be a good fit on AI Town's engine today. ================================================ FILE: Dockerfile ================================================ # Use an Ubuntu base image FROM ubuntu:22.04 # Install dependencies RUN apt-get update && \ apt-get install -y \ curl \ python3 \ python3-pip \ unzip \ socat \ build-essential \ libssl-dev \ iproute2 \ && rm -rf /var/lib/apt/lists/* # Install NVM, Node.js, and npm RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash && \ export NVM_DIR="$HOME/.nvm" && \ [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && \ nvm install 18 && \ nvm use 18 # Add NVM to PATH ENV NVM_DIR /root/.nvm ENV NODE_VERSION 18.0.0 RUN . $NVM_DIR/nvm.sh && nvm install $NODE_VERSION ENV NODE_PATH $NVM_DIR/versions/node/v$NODE_VERSION/lib/node_modules ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH # Set the working directory WORKDIR /usr/src/app # Copy dependency files COPY package*.json ./ # Install npm dependencies RUN npm install RUN npx update-browserslist-db@latest # Copy application files COPY . . # Expose necessary ports EXPOSE 5173 CMD ["npx", "vite", "--host"] ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 a16z-infra Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # AI Town 🏠💻💌 [Live Demo](https://www.convex.dev/ai-town) [Join our community Discord: AI Stack Devs](https://discord.gg/PQUmTBTGmT) Screen Shot 2023-08-14 at 10 01 00 AM AI Town is a virtual town where AI characters live, chat and socialize. This project is a deployable starter kit for easily building and customizing your own version of AI town. Inspired by the research paper [_Generative Agents: Interactive Simulacra of Human Behavior_](https://arxiv.org/pdf/2304.03442.pdf). The primary goal of this project, beyond just being a lot of fun to work on, is to provide a platform with a strong foundation that is meant to be extended. The back-end natively supports shared global state, transactions, and a simulation engine and should be suitable from everything from a simple project to play around with to a scalable, multi-player game. A secondary goal is to make a JS/TS framework available as most simulators in this space (including the original paper above) are written in Python. ## Overview - 💻 [Stack](#stack) - 🧠 [Installation](#installation) (cloud, local, Docker, self-host, Fly.io, ...) - 💻️ [Windows Pre-requisites](#windows-installation) - 🤖 [Configure your LLM of choice](#connect-an-llm) (Ollama, OpenAI, Together.ai, ...) - 👤 [Customize - YOUR OWN simulated world](#customize-your-own-simulation) - 👩‍💻 [Deploying to production](#deploy-the-app-to-production) - 🐛 [Troubleshooting](#troubleshooting) ## Stack - Game engine, database, and vector search: [Convex](https://convex.dev/) - Auth (Optional): [Clerk](https://clerk.com/) - Default chat model is `llama3` and embeddings with `mxbai-embed-large`. - Local inference: [Ollama](https://github.com/jmorganca/ollama) - Configurable for other cloud LLMs: [Together.ai](https://together.ai/) or anything that speaks the [OpenAI API](https://platform.openai.com/). PRs welcome to add more cloud provider support. - Background Music Generation: [Replicate](https://replicate.com/) using [MusicGen](https://huggingface.co/spaces/facebook/MusicGen) Other credits: - Pixel Art Generation: [Replicate](https://replicate.com/), [Fal.ai](https://serverless.fal.ai/lora) - All interactions, background music and rendering on the component in the project are powered by [PixiJS](https://pixijs.com/). - Tilesheet: - https://opengameart.org/content/16x16-game-assets by George Bailey - https://opengameart.org/content/16x16-rpg-tileset by hilau - We used https://github.com/pierpo/phaser3-simple-rpg for the original POC of this project. We have since re-wrote the whole app, but appreciated the easy starting point - Original assets by [ansimuz](https://opengameart.org/content/tiny-rpg-forest) - The UI is based on original assets by [Mounir Tohami](https://mounirtohami.itch.io/pixel-art-gui-elements) # Installation The overall steps are: 1. [Build and deploy](#build-and-deploy) 2. [Connect it to an LLM](#connect-an-llm) ## Build and Deploy There are a few ways to run the app on top of Convex (the backend). 1. The standard Convex setup, where you develop locally or in the cloud. This requires a Convex account(free). This is the easiest way to depoy it to the cloud and seriously develop. 2. If you want to try it out without an account and you're okay with Docker, the Docker Compose setup is nice and self-contained. 3. There's a community fork of this project offering a one-click install on [Pinokio](https://pinokio.computer/item?uri=https://github.com/cocktailpeanutlabs/aitown) for anyone interested in running but not modifying it 😎. 4. You can also deploy it to [Fly.io](https://fly.io/). See [./fly](./fly) for instructions. ### Standard Setup Note, if you're on Windows, see [below](#windows-installation). ```sh git clone https://github.com/a16z-infra/ai-town.git cd ai-town npm install ``` This will require logging into your Convex account, if you haven't already. To run it: ```sh npm run dev ``` You can now visit http://localhost:5173. If you'd rather run the frontend and backend separately (which syncs your backend functions as they're saved), you can run these in two terminals: ```bash npm run dev:frontend npm run dev:backend ``` See [package.json](./package.json) for details. ### Using Docker Compose with self-hosted Convex You can also run the Convex backend with the self-hosted Docker container. Here we'll set it up to run the frontend, backend, and dashboard all via docker compose. ```sh docker compose up --build -d ``` The container will keep running in the background if you pass `-d`. After you've done it once, you can `stop` and `start` services. - The frontend will be running on http://localhost:5173. - The backend will be running on http://localhost:3210 (3211 for the http api). - The dashboard will be running on http://localhost:6791. To log into the dashboard and deploy from the convex CLI, you will need to generate an admin key. ```sh docker compose exec backend ./generate_admin_key.sh ``` Add it to your `.env.local` file. Note: If you run `down` and `up`, you'll have to generate the key again and update the `.env.local` file. ```sh # in .env.local CONVEX_SELF_HOSTED_ADMIN_KEY="" # Ensure there are quotes around it CONVEX_SELF_HOSTED_URL="http://127.0.0.1:3210" ``` Then set up the Convex backend (one time): ```sh npm run predev ``` To continuously deploy new code to the backend and print logs: ```sh npm run dev:backend ``` To see the dashboard, visit `http://localhost:6791` and provide the admin key you generated earlier. ### Configuring Docker for Ollama If you'll be using Ollama for local inference, you'll need to configure Docker to connect to it. ```sh npx convex env set OLLAMA_HOST http://host.docker.internal:11434 ``` To test the connection (after you [have it running](#ollama-default)): ```sh docker compose exec backend /bin/bash curl http://host.docker.internal:11434 ``` If it says "Ollama is running", it's good! Otherwise, check out the [Troubleshooting](#troubleshooting) section. ## Connect an LLM Note: If you want to run the backend in the cloud, you can either use a cloud-based LLM API, like OpenAI or Together.ai or you can proxy the traffic from the cloud to your local Ollama. See [below](#using-local-inference-from-a-cloud-deployment) for instructions. ### Ollama (default) By default, the app tries to use Ollama to run it entirely locally. 1. Download and install [Ollama](https://ollama.com/). 2. Open the app or run `ollama serve` in a terminal. `ollama serve` will warn you if the app is already running. 3. Run `ollama pull llama3` to have it download `llama3`. 4. Test it out with `ollama run llama3`. Ollama model options can be found [here](https://ollama.ai/library). If you want to customize which model to use, adjust convex/util/llm.ts or set `npx convex env set OLLAMA_MODEL # model`. If you want to edit the embedding model: 1. Change the `OLLAMA_EMBEDDING_DIMENSION` in `convex/util/llm.ts` and ensure: `export const EMBEDDING_DIMENSION = OLLAMA_EMBEDDING_DIMENSION;` 2. Set `npx convex env set OLLAMA_EMBEDDING_MODEL # model`. Note: You might want to set `NUM_MEMORIES_TO_SEARCH` to `1` in constants.ts, to reduce the size of conversation prompts, if you see slowness. ### OpenAI To use OpenAI, you need to: ```ts // In convex/util/llm.ts change the following line: export const EMBEDDING_DIMENSION = OPENAI_EMBEDDING_DIMENSION; ``` Set the `OPENAI_API_KEY` environment variable. Visit https://platform.openai.com/account/api-keys if you don't have one. ```sh npx convex env set OPENAI_API_KEY 'your-key' ``` Optional: choose models with `OPENAI_CHAT_MODEL` and `OPENAI_EMBEDDING_MODEL`. ### Together.ai To use Together.ai, you need to: ```ts // In convex/util/llm.ts change the following line: export const EMBEDDING_DIMENSION = TOGETHER_EMBEDDING_DIMENSION; ``` Set the `TOGETHER_API_KEY` environment variable. Visit https://api.together.xyz/settings/api-keys if you don't have one. ```sh npx convex env set TOGETHER_API_KEY 'your-key' ``` Optional: choose models via `TOGETHER_CHAT_MODEL`, `TOGETHER_EMBEDDING_MODEL`. The embedding model's dimension must match `EMBEDDING_DIMENSION`. ### Other OpenAI-compatible API You can use any OpenAI-compatible API, such as Anthropic, Groq, or Azure. - Change the `EMBEDDING_DIMENSION` in `convex/util/llm.ts` to match the dimension of your embedding model. - Edit `getLLMConfig` in `llm.ts` or set environment variables: ```sh npx convex env set LLM_API_URL 'your-url' npx convex env set LLM_API_KEY 'your-key' npx convex env set LLM_MODEL 'your-chat-model' npx convex env set LLM_EMBEDDING_MODEL 'your-embedding-model' ``` Note: if `LLM_API_KEY` is not required, don't set it. ### Note on changing the LLM provider or embedding model: If you change the LLM provider or embedding model, you should delete your data and start over. The embeddings used for memory are based on the embedding model you choose, and the dimension of the vector database must match the embedding model's dimension. See [below](#wiping-the-database-and-starting-over) for how to do that. ## Customize your own simulation NOTE: every time you change character data, you should re-run `npx convex run testing:wipeAllTables` and then `npm run dev` to re-upload everything to Convex. This is because character data is sent to Convex on the initial load. However, beware that `npx convex run testing:wipeAllTables` WILL wipe all of your data. 1. Create your own characters and stories: All characters and stories, as well as their spritesheet references are stored in [characters.ts](./data/characters.ts). You can start by changing character descriptions. 2. Updating spritesheets: in `data/characters.ts`, you will see this code: ```ts export const characters = [ { name: 'f1', textureUrl: '/assets/32x32folk.png', spritesheetData: f1SpritesheetData, speed: 0.1, }, ... ]; ``` You should find a sprite sheet for your character, and define sprite motion / assets in the corresponding file (in the above example, `f1SpritesheetData` was defined in f1.ts) 3. Update the Background (Environment): The map gets loaded in `convex/init.ts` from `data/gentle.js`. To update the map, follow these steps: - Use [Tiled](https://www.mapeditor.org/) to export tilemaps as a JSON file (2 layers named bgtiles and objmap) - Use the `convertMap.js` script to convert the JSON to a format that the engine can use. ```console node data/convertMap.js ``` - ``: Path to the Tiled JSON file. - ``: Path to tileset images. - ``: Tileset width in pixels. - ``: Tileset height in pixels. Generates `converted-map.js` that you can use like `gentle.js` 4. Adding background music with Replicate (Optional) For Daily background music generation, create a [Replicate](https://replicate.com/) account and create a token in your Profile's [API Token page](https://replicate.com/account/api-tokens). `npx convex env set REPLICATE_API_TOKEN # token` This only works if you can receive the webhook from Replicate. If it's running in the normal Convex cloud, it will work by default. If you're self-hosting, you'll need to configure it to hit your app's url on `/http`. If you're using Docker Compose, it will be `http://localhost:3211`, but you'll need to proxy the traffic to your local machine. **Note**: The simulation will pause after 5 minutes if the window is idle. Loading the page will unpause it. You can also manually freeze & unfreeze the world with a button in the UI. If you want to run the world without the browser, you can comment-out the "stop inactive worlds" cron in `convex/crons.ts`. - Change the background music by modifying the prompt in `convex/music.ts` - Change how often to generate new music at `convex/crons.ts` by modifying the `generate new background music` job ## Commands to run / test / debug **To stop the back end, in case of too much activity** This will stop running the engine and agents. You can still run queries and run functions to debug. ```bash npx convex run testing:stop ``` **To restart the back end after stopping it** ```bash npx convex run testing:resume ``` **To kick the engine in case the game engine or agents aren't running** ```bash npx convex run testing:kick ``` **To archive the world** If you'd like to reset the world and start from scratch, you can archive the current world: ```bash npx convex run testing:archive ``` Then, you can still look at the world's data in the dashboard, but the engine and agents will no longer run. You can then create a fresh world with `init`. ```bash npx convex run init ``` **To pause your backend deployment** You can go to the [dashboard](https://dashboard.convex.dev) to your deployment settings to pause and un-pause your deployment. This will stop all functions, whether invoked from the client, scheduled, or as a cron job. See this as a last resort, as there are gentler ways of stopping above. ## Windows Installation ### Prerequisites 1. **Windows 10/11 with WSL2 installed** 2. **Internet connection** Steps: 1. Install WSL2 First, you need to install WSL2. Follow [this guide](https://docs.microsoft.com/en-us/windows/wsl/install) to set up WSL2 on your Windows machine. We recommend using Ubuntu as your Linux distribution. 2. Update Packages Open your WSL terminal (Ubuntu) and update your packages: ```sh sudo apt update ``` 3. Install NVM and Node.js NVM (Node Version Manager) helps manage multiple versions of Node.js. Install NVM and Node.js 18 (the stable version): ```sh curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" source ~/.bashrc nvm install 18 nvm use 18 ``` 4. Install Python and Pip Python is required for some dependencies. Install Python and Pip: ```sh sudo apt-get install python3 python3-pip sudo ln -s /usr/bin/python3 /usr/bin/python ``` At this point, you can follow the instructions [above](#installation). ## Deploy the app to production ### Deploy Convex functions to prod environment Before you can run the app, you will need to make sure the Convex functions are deployed to its production environment. Note: this is assuming you're using the default Convex cloud product. 1. Run `npx convex deploy` to deploy the convex functions to production 2. Run `npx convex run init --prod` To transfer your local data to the cloud, you can run `npx convex export` and then import it with `npx convex import --prod`. If you have existing data you want to clear, you can run `npx convex run testing:wipeAllTables --prod` ### Adding Auth (Optional) You can add clerk auth back in with `git revert b44a436`. Or just look at that diff for what changed to remove it. **Make a Clerk account** - Go to https://dashboard.clerk.com/ and click on "Add Application" - Name your application and select the sign-in providers you would like to offer users - Create Application - Add `VITE_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` to `.env.local` ```bash VITE_CLERK_PUBLISHABLE_KEY=pk_*** CLERK_SECRET_KEY=sk_*** ``` - Go to JWT Templates and create a new Convex Template. - Copy the JWKS endpoint URL for use below. ```sh npx convex env set CLERK_ISSUER_URL # e.g. https://your-issuer-url.clerk.accounts.dev/ ``` ### Deploy the frontend to Vercel - Register an account on Vercel and then [install the Vercel CLI](https://vercel.com/docs/cli). - **If you are using Github Codespaces**: You will need to [install the Vercel CLI](https://vercel.com/docs/cli) and authenticate from your codespaces cli by running `vercel login`. - Deploy the app to Vercel with `vercel --prod`. ## Using local inference from a cloud deployment We support using [Ollama](https://github.com/jmorganca/ollama) for conversation generations. To have it accessible from the web, you can use Tunnelmole or Ngrok or similar so the cloud backend can send requests to Ollama running on your local machine. Steps: 1. Set up either Tunnelmole or Ngrok. 2. Add Ollama endpoint to Convex ```sh npx convex env set OLLAMA_HOST # your tunnelmole/ngrok unique url from the previous step ``` 3. Update Ollama domains Ollama has a list of accepted domains. Add the ngrok domain so it won't reject traffic. see [ollama.ai](https://ollama.ai) for more details. ### Using Tunnelmole [Tunnelmole](https://github.com/robbie-cahill/tunnelmole-client) is an open source tunneling tool. You can install Tunnelmole using one of the following options: - NPM: `npm install -g tunnelmole` - Linux: `curl -s https://tunnelmole.com/sh/install-linux.sh | sudo bash` - Mac: `curl -s https://tunnelmole.com/sh/install-mac.sh --output install-mac.sh && sudo bash install-mac.sh` - Windows: Install with NPM, or if you don't have NodeJS installed, download the `exe` file for Windows [here](https://tunnelmole.com/downloads/tmole.exe) and put it somewhere in your PATH. Once Tunnelmole is installed, run the following command: ``` tmole 11434 ``` Tunnelmole should output a unique url once you run this command. ### Using Ngrok Ngrok is a popular closed source tunneling tool. - [Install Ngrok](https://ngrok.com/docs/getting-started/) Once ngrok is installed and authenticated, run the following command: ``` ngrok http http://localhost:11434 ``` Ngrok should output a unique url once you run this command. ## Troubleshooting ### Wiping the database and starting over You can wipe the database by running: ```sh npx convex run testing:wipeAllTables ``` Then reset with: ```sh npx convex run init ``` ### Incompatible Node.js versions If you encounter a node version error on the convex server upon application startup, please use node version 18, which is the most stable. One way to do this is by [installing nvm](https://nodejs.org/en/download/package-manager) and running `nvm install 18` and `nvm use 18`. ### Reaching Ollama If you're having trouble with the backend communicating with Ollama, it depends on your setup how to debug: 1. If you're running directly on Windows, see [Windows Ollama connection issues](#windows-ollama-connection-issues). 2. If you're using **Docker**, see [Docker to Ollama connection issues](#docker-to-ollama-connection-issues). 3. If you're running locally, you can try the following: ```sh npx convex env set OLLAMA_HOST http://localhost:11434 ``` By default, the host is set to `http://127.0.0.1:11434`. Some systems prefer `localhost` ¯\_(ツ)\_/¯. ### Windows Ollama connection issues If the above didn't work after following the [windows](#windows-installation) and regular [installation](#installation) instructions, you can try the following, assuming you're **not** using Docker. If you're using Docker, see the [next section](#docker-to-ollama-connection-issues) for Docker troubleshooting. For running directly on Windows, you can try the following: 1. Install `unzip` and `socat`: ```sh sudo apt install unzip socat ``` 2. Configure `socat` to Bridge Ports for Ollama Run the following command to bridge ports: ```sh socat TCP-LISTEN:11434,fork TCP:$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):11434 & ``` 3. Test if it's working: ```sh curl http://127.0.0.1:11434 ``` If it responds OK, the Ollama API should be accessible. ### Docker to Ollama connection issues If you're having trouble with the backend communicating with Ollama, there's a couple things to check: 1. Is Docker at least verion 18.03 ? That allows you to use the `host.docker.internal` hostname to connect to the host from inside the container. 2. Is Ollama running? You can check this by running `curl http://localhost:11434` from outside the container. 3. Is Ollama accessible from inside the container? You can check this by running `docker compose exec backend curl http://host.docker.internal:11434`. If 1 & 2 work, but 3 does not, you can use `socat` to bridge the traffic from inside the container to Ollama running on the host. 1. Configure `socat` with the host's IP address (not the Docker IP). ```sh docker compose exec backend /bin/bash HOST_IP=YOUR-HOST-IP socat TCP-LISTEN:11434,fork TCP:$HOST_IP:11434 ``` Keep this running. 2. Then from outside of the container: ```sh npx convex env set OLLAMA_HOST http://localhost:11434 ``` 3. Test if it's working: ```sh docker compose exec backend curl http://localhost:11434 ``` If it responds OK, the Ollama API is accessible. Otherwise, try changing the previous two to `http://127.0.0.1:11434`. ### Launching an Interactive Docker Terminal If you wan to investigate inside the container, you can launch an interactive Docker terminal, for the `frontend`, `backend` or `dashboard` service: ```bash docker compose exec frontend /bin/bash ``` To exit the container, run `exit`. ### Updating the browser list ```bash docker compose exec frontend npx update-browserslist-db@latest ``` # 🧑‍🏫 What is Convex? [Convex](https://convex.dev) is a hosted backend platform with a built-in database that lets you write your [database schema](https://docs.convex.dev/database/schemas) and [server functions](https://docs.convex.dev/functions) in [TypeScript](https://docs.convex.dev/typescript). Server-side database [queries](https://docs.convex.dev/functions/query-functions) automatically [cache](https://docs.convex.dev/functions/query-functions#caching--reactivity) and [subscribe](https://docs.convex.dev/client/react#reactivity) to data, powering a [realtime `useQuery` hook](https://docs.convex.dev/client/react#fetching-data) in our [React client](https://docs.convex.dev/client/react). There are also clients for [Python](https://docs.convex.dev/client/python), [Rust](https://docs.convex.dev/client/rust), [ReactNative](https://docs.convex.dev/client/react-native), and [Node](https://docs.convex.dev/client/javascript), as well as a straightforward [HTTP API](https://docs.convex.dev/http-api/). The database supports [NoSQL-style documents](https://docs.convex.dev/database/document-storage) with [opt-in schema validation](https://docs.convex.dev/database/schemas), [relationships](https://docs.convex.dev/database/document-ids) and [custom indexes](https://docs.convex.dev/database/indexes/) (including on fields in nested objects). The [`query`](https://docs.convex.dev/functions/query-functions) and [`mutation`](https://docs.convex.dev/functions/mutation-functions) server functions have transactional, low latency access to the database and leverage our [`v8` runtime](https://docs.convex.dev/functions/runtimes) with [determinism guardrails](https://docs.convex.dev/functions/runtimes#using-randomness-and-time-in-queries-and-mutations) to provide the strongest ACID guarantees on the market: immediate consistency, serializable isolation, and automatic conflict resolution via [optimistic multi-version concurrency control](https://docs.convex.dev/database/advanced/occ) (OCC / MVCC). The [`action` server functions](https://docs.convex.dev/functions/actions) have access to external APIs and enable other side-effects and non-determinism in either our [optimized `v8` runtime](https://docs.convex.dev/functions/runtimes) or a more [flexible `node` runtime](https://docs.convex.dev/functions/runtimes#nodejs-runtime). Functions can run in the background via [scheduling](https://docs.convex.dev/scheduling/scheduled-functions) and [cron jobs](https://docs.convex.dev/scheduling/cron-jobs). Development is cloud-first, with [hot reloads for server function](https://docs.convex.dev/cli#run-the-convex-dev-server) editing via the [CLI](https://docs.convex.dev/cli), [preview deployments](https://docs.convex.dev/production/hosting/preview-deployments), [logging and exception reporting integrations](https://docs.convex.dev/production/integrations/), There is a [dashboard UI](https://docs.convex.dev/dashboard) to [browse and edit data](https://docs.convex.dev/dashboard/deployments/data), [edit environment variables](https://docs.convex.dev/production/environment-variables), [view logs](https://docs.convex.dev/dashboard/deployments/logs), [run server functions](https://docs.convex.dev/dashboard/deployments/functions), and more. There are built-in features for [reactive pagination](https://docs.convex.dev/database/pagination), [file storage](https://docs.convex.dev/file-storage), [reactive text search](https://docs.convex.dev/text-search), [vector search](https://docs.convex.dev/vector-search), [https endpoints](https://docs.convex.dev/functions/http-actions) (for webhooks), [snapshot import/export](https://docs.convex.dev/database/import-export/), [streaming import/export](https://docs.convex.dev/production/integrations/streaming-import-export), and [runtime validation](https://docs.convex.dev/database/schemas#validators) for [function arguments](https://docs.convex.dev/functions/args-validation) and [database data](https://docs.convex.dev/database/schemas#schema-validation). Everything scales automatically, and it’s [free to start](https://www.convex.dev/plans). ================================================ FILE: convex/_generated/api.d.ts ================================================ /* eslint-disable */ /** * Generated `api` utility. * * THIS CODE IS AUTOMATICALLY GENERATED. * * To regenerate, run `npx convex dev`. * @module */ import type { ApiFromModules, FilterApi, FunctionReference, } from "convex/server"; import type * as agent_conversation from "../agent/conversation.js"; import type * as agent_embeddingsCache from "../agent/embeddingsCache.js"; import type * as agent_memory from "../agent/memory.js"; import type * as aiTown_agent from "../aiTown/agent.js"; import type * as aiTown_agentDescription from "../aiTown/agentDescription.js"; import type * as aiTown_agentInputs from "../aiTown/agentInputs.js"; import type * as aiTown_agentOperations from "../aiTown/agentOperations.js"; import type * as aiTown_conversation from "../aiTown/conversation.js"; import type * as aiTown_conversationMembership from "../aiTown/conversationMembership.js"; import type * as aiTown_game from "../aiTown/game.js"; import type * as aiTown_ids from "../aiTown/ids.js"; import type * as aiTown_inputHandler from "../aiTown/inputHandler.js"; import type * as aiTown_inputs from "../aiTown/inputs.js"; import type * as aiTown_insertInput from "../aiTown/insertInput.js"; import type * as aiTown_location from "../aiTown/location.js"; import type * as aiTown_main from "../aiTown/main.js"; import type * as aiTown_movement from "../aiTown/movement.js"; import type * as aiTown_player from "../aiTown/player.js"; import type * as aiTown_playerDescription from "../aiTown/playerDescription.js"; import type * as aiTown_world from "../aiTown/world.js"; import type * as aiTown_worldMap from "../aiTown/worldMap.js"; import type * as constants from "../constants.js"; import type * as crons from "../crons.js"; import type * as engine_abstractGame from "../engine/abstractGame.js"; import type * as engine_historicalObject from "../engine/historicalObject.js"; import type * as http from "../http.js"; import type * as init from "../init.js"; import type * as messages from "../messages.js"; import type * as music from "../music.js"; import type * as testing from "../testing.js"; import type * as util_FastIntegerCompression from "../util/FastIntegerCompression.js"; import type * as util_assertNever from "../util/assertNever.js"; import type * as util_asyncMap from "../util/asyncMap.js"; import type * as util_compression from "../util/compression.js"; import type * as util_geometry from "../util/geometry.js"; import type * as util_isSimpleObject from "../util/isSimpleObject.js"; import type * as util_llm from "../util/llm.js"; import type * as util_minheap from "../util/minheap.js"; import type * as util_object from "../util/object.js"; import type * as util_sleep from "../util/sleep.js"; import type * as util_types from "../util/types.js"; import type * as util_xxhash from "../util/xxhash.js"; import type * as world from "../world.js"; /** * A utility for referencing Convex functions in your app's API. * * Usage: * ```js * const myFunctionReference = api.myModule.myFunction; * ``` */ declare const fullApi: ApiFromModules<{ "agent/conversation": typeof agent_conversation; "agent/embeddingsCache": typeof agent_embeddingsCache; "agent/memory": typeof agent_memory; "aiTown/agent": typeof aiTown_agent; "aiTown/agentDescription": typeof aiTown_agentDescription; "aiTown/agentInputs": typeof aiTown_agentInputs; "aiTown/agentOperations": typeof aiTown_agentOperations; "aiTown/conversation": typeof aiTown_conversation; "aiTown/conversationMembership": typeof aiTown_conversationMembership; "aiTown/game": typeof aiTown_game; "aiTown/ids": typeof aiTown_ids; "aiTown/inputHandler": typeof aiTown_inputHandler; "aiTown/inputs": typeof aiTown_inputs; "aiTown/insertInput": typeof aiTown_insertInput; "aiTown/location": typeof aiTown_location; "aiTown/main": typeof aiTown_main; "aiTown/movement": typeof aiTown_movement; "aiTown/player": typeof aiTown_player; "aiTown/playerDescription": typeof aiTown_playerDescription; "aiTown/world": typeof aiTown_world; "aiTown/worldMap": typeof aiTown_worldMap; constants: typeof constants; crons: typeof crons; "engine/abstractGame": typeof engine_abstractGame; "engine/historicalObject": typeof engine_historicalObject; http: typeof http; init: typeof init; messages: typeof messages; music: typeof music; testing: typeof testing; "util/FastIntegerCompression": typeof util_FastIntegerCompression; "util/assertNever": typeof util_assertNever; "util/asyncMap": typeof util_asyncMap; "util/compression": typeof util_compression; "util/geometry": typeof util_geometry; "util/isSimpleObject": typeof util_isSimpleObject; "util/llm": typeof util_llm; "util/minheap": typeof util_minheap; "util/object": typeof util_object; "util/sleep": typeof util_sleep; "util/types": typeof util_types; "util/xxhash": typeof util_xxhash; world: typeof world; }>; export declare const api: FilterApi< typeof fullApi, FunctionReference >; export declare const internal: FilterApi< typeof fullApi, FunctionReference >; ================================================ FILE: convex/_generated/api.js ================================================ /* eslint-disable */ /** * Generated `api` utility. * * THIS CODE IS AUTOMATICALLY GENERATED. * * To regenerate, run `npx convex dev`. * @module */ import { anyApi } from "convex/server"; /** * A utility for referencing Convex functions in your app's API. * * Usage: * ```js * const myFunctionReference = api.myModule.myFunction; * ``` */ export const api = anyApi; export const internal = anyApi; ================================================ FILE: convex/_generated/dataModel.d.ts ================================================ /* eslint-disable */ /** * Generated data model types. * * THIS CODE IS AUTOMATICALLY GENERATED. * * To regenerate, run `npx convex dev`. * @module */ import type { DataModelFromSchemaDefinition, DocumentByName, TableNamesInDataModel, SystemTableNames, } from "convex/server"; import type { GenericId } from "convex/values"; import schema from "../schema.js"; /** * The names of all of your Convex tables. */ export type TableNames = TableNamesInDataModel; /** * The type of a document stored in Convex. * * @typeParam TableName - A string literal type of the table name (like "users"). */ export type Doc = DocumentByName< DataModel, TableName >; /** * An identifier for a document in Convex. * * Convex documents are uniquely identified by their `Id`, which is accessible * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids). * * Documents can be loaded using `db.get(id)` in query and mutation functions. * * IDs are just strings at runtime, but this type can be used to distinguish them from other * strings when type checking. * * @typeParam TableName - A string literal type of the table name (like "users"). */ export type Id = GenericId; /** * A type describing your Convex data model. * * This type includes information about what tables you have, the type of * documents stored in those tables, and the indexes defined on them. * * This type is used to parameterize methods like `queryGeneric` and * `mutationGeneric` to make them type-safe. */ export type DataModel = DataModelFromSchemaDefinition; ================================================ FILE: convex/_generated/server.d.ts ================================================ /* eslint-disable */ /** * Generated utilities for implementing server-side Convex query and mutation functions. * * THIS CODE IS AUTOMATICALLY GENERATED. * * To regenerate, run `npx convex dev`. * @module */ import { ActionBuilder, HttpActionBuilder, MutationBuilder, QueryBuilder, GenericActionCtx, GenericMutationCtx, GenericQueryCtx, GenericDatabaseReader, GenericDatabaseWriter, } from "convex/server"; import type { DataModel } from "./dataModel.js"; /** * Define a query in this Convex app's public API. * * This function will be allowed to read your Convex database and will be accessible from the client. * * @param func - The query function. It receives a {@link QueryCtx} as its first argument. * @returns The wrapped query. Include this as an `export` to name it and make it accessible. */ export declare const query: QueryBuilder; /** * Define a query that is only accessible from other Convex functions (but not from the client). * * This function will be allowed to read from your Convex database. It will not be accessible from the client. * * @param func - The query function. It receives a {@link QueryCtx} as its first argument. * @returns The wrapped query. Include this as an `export` to name it and make it accessible. */ export declare const internalQuery: QueryBuilder; /** * Define a mutation in this Convex app's public API. * * This function will be allowed to modify your Convex database and will be accessible from the client. * * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. */ export declare const mutation: MutationBuilder; /** * Define a mutation that is only accessible from other Convex functions (but not from the client). * * This function will be allowed to modify your Convex database. It will not be accessible from the client. * * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. */ export declare const internalMutation: MutationBuilder; /** * Define an action in this Convex app's public API. * * An action is a function which can execute any JavaScript code, including non-deterministic * code and code with side-effects, like calling third-party services. * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. * * @param func - The action. It receives an {@link ActionCtx} as its first argument. * @returns The wrapped action. Include this as an `export` to name it and make it accessible. */ export declare const action: ActionBuilder; /** * Define an action that is only accessible from other Convex functions (but not from the client). * * @param func - The function. It receives an {@link ActionCtx} as its first argument. * @returns The wrapped function. Include this as an `export` to name it and make it accessible. */ export declare const internalAction: ActionBuilder; /** * Define an HTTP action. * * This function will be used to respond to HTTP requests received by a Convex * deployment if the requests matches the path and method where this action * is routed. Be sure to route your action in `convex/http.js`. * * @param func - The function. It receives an {@link ActionCtx} as its first argument. * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. */ export declare const httpAction: HttpActionBuilder; /** * A set of services for use within Convex query functions. * * The query context is passed as the first argument to any Convex query * function run on the server. * * This differs from the {@link MutationCtx} because all of the services are * read-only. */ export type QueryCtx = GenericQueryCtx; /** * A set of services for use within Convex mutation functions. * * The mutation context is passed as the first argument to any Convex mutation * function run on the server. */ export type MutationCtx = GenericMutationCtx; /** * A set of services for use within Convex action functions. * * The action context is passed as the first argument to any Convex action * function run on the server. */ export type ActionCtx = GenericActionCtx; /** * An interface to read from the database within Convex query functions. * * The two entry points are {@link DatabaseReader.get}, which fetches a single * document by its {@link Id}, or {@link DatabaseReader.query}, which starts * building a query. */ export type DatabaseReader = GenericDatabaseReader; /** * An interface to read from and write to the database within Convex mutation * functions. * * Convex guarantees that all writes within a single mutation are * executed atomically, so you never have to worry about partial writes leaving * your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control) * for the guarantees Convex provides your functions. */ export type DatabaseWriter = GenericDatabaseWriter; ================================================ FILE: convex/_generated/server.js ================================================ /* eslint-disable */ /** * Generated utilities for implementing server-side Convex query and mutation functions. * * THIS CODE IS AUTOMATICALLY GENERATED. * * To regenerate, run `npx convex dev`. * @module */ import { actionGeneric, httpActionGeneric, queryGeneric, mutationGeneric, internalActionGeneric, internalMutationGeneric, internalQueryGeneric, } from "convex/server"; /** * Define a query in this Convex app's public API. * * This function will be allowed to read your Convex database and will be accessible from the client. * * @param func - The query function. It receives a {@link QueryCtx} as its first argument. * @returns The wrapped query. Include this as an `export` to name it and make it accessible. */ export const query = queryGeneric; /** * Define a query that is only accessible from other Convex functions (but not from the client). * * This function will be allowed to read from your Convex database. It will not be accessible from the client. * * @param func - The query function. It receives a {@link QueryCtx} as its first argument. * @returns The wrapped query. Include this as an `export` to name it and make it accessible. */ export const internalQuery = internalQueryGeneric; /** * Define a mutation in this Convex app's public API. * * This function will be allowed to modify your Convex database and will be accessible from the client. * * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. */ export const mutation = mutationGeneric; /** * Define a mutation that is only accessible from other Convex functions (but not from the client). * * This function will be allowed to modify your Convex database. It will not be accessible from the client. * * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. */ export const internalMutation = internalMutationGeneric; /** * Define an action in this Convex app's public API. * * An action is a function which can execute any JavaScript code, including non-deterministic * code and code with side-effects, like calling third-party services. * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. * * @param func - The action. It receives an {@link ActionCtx} as its first argument. * @returns The wrapped action. Include this as an `export` to name it and make it accessible. */ export const action = actionGeneric; /** * Define an action that is only accessible from other Convex functions (but not from the client). * * @param func - The function. It receives an {@link ActionCtx} as its first argument. * @returns The wrapped function. Include this as an `export` to name it and make it accessible. */ export const internalAction = internalActionGeneric; /** * Define a Convex HTTP action. * * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object * as its second. * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`. */ export const httpAction = httpActionGeneric; ================================================ FILE: convex/agent/conversation.ts ================================================ import { v } from 'convex/values'; import { Id } from '../_generated/dataModel'; import { ActionCtx, internalQuery } from '../_generated/server'; import { LLMMessage, chatCompletion } from '../util/llm'; import * as memory from './memory'; import { api, internal } from '../_generated/api'; import * as embeddingsCache from './embeddingsCache'; import { GameId, conversationId, playerId } from '../aiTown/ids'; import { NUM_MEMORIES_TO_SEARCH } from '../constants'; const selfInternal = internal.agent.conversation; export async function startConversationMessage( ctx: ActionCtx, worldId: Id<'worlds'>, conversationId: GameId<'conversations'>, playerId: GameId<'players'>, otherPlayerId: GameId<'players'>, ): Promise { const { player, otherPlayer, agent, otherAgent, lastConversation } = await ctx.runQuery( selfInternal.queryPromptData, { worldId, playerId, otherPlayerId, conversationId, }, ); const embedding = await embeddingsCache.fetch( ctx, `${player.name} is talking to ${otherPlayer.name}`, ); const memories = await memory.searchMemories( ctx, player.id as GameId<'players'>, embedding, Number(process.env.NUM_MEMORIES_TO_SEARCH) || NUM_MEMORIES_TO_SEARCH, ); const memoryWithOtherPlayer = memories.find( (m) => m.data.type === 'conversation' && m.data.playerIds.includes(otherPlayerId), ); const prompt = [ `You are ${player.name}, and you just started a conversation with ${otherPlayer.name}.`, ]; prompt.push(...agentPrompts(otherPlayer, agent, otherAgent ?? null)); prompt.push(...previousConversationPrompt(otherPlayer, lastConversation)); prompt.push(...relatedMemoriesPrompt(memories)); if (memoryWithOtherPlayer) { prompt.push( `Be sure to include some detail or question about a previous conversation in your greeting.`, ); } const lastPrompt = `${player.name} to ${otherPlayer.name}:`; prompt.push(lastPrompt); const { content } = await chatCompletion({ messages: [ { role: 'system', content: prompt.join('\n'), }, ], max_tokens: 300, stop: stopWords(otherPlayer.name, player.name), }); return trimContentPrefx(content, lastPrompt); } function trimContentPrefx(content: string, prompt: string) { if (content.startsWith(prompt)) { return content.slice(prompt.length).trim(); } return content; } export async function continueConversationMessage( ctx: ActionCtx, worldId: Id<'worlds'>, conversationId: GameId<'conversations'>, playerId: GameId<'players'>, otherPlayerId: GameId<'players'>, ): Promise { const { player, otherPlayer, conversation, agent, otherAgent } = await ctx.runQuery( selfInternal.queryPromptData, { worldId, playerId, otherPlayerId, conversationId, }, ); const now = Date.now(); const started = new Date(conversation.created); const embedding = await embeddingsCache.fetch( ctx, `What do you think about ${otherPlayer.name}?`, ); const memories = await memory.searchMemories(ctx, player.id as GameId<'players'>, embedding, 3); const prompt = [ `You are ${player.name}, and you're currently in a conversation with ${otherPlayer.name}.`, `The conversation started at ${started.toLocaleString()}. It's now ${now.toLocaleString()}.`, ]; prompt.push(...agentPrompts(otherPlayer, agent, otherAgent ?? null)); prompt.push(...relatedMemoriesPrompt(memories)); prompt.push( `Below is the current chat history between you and ${otherPlayer.name}.`, `DO NOT greet them again. Do NOT use the word "Hey" too often. Your response should be brief and within 200 characters.`, ); const llmMessages: LLMMessage[] = [ { role: 'system', content: prompt.join('\n'), }, ...(await previousMessages( ctx, worldId, player, otherPlayer, conversation.id as GameId<'conversations'>, )), ]; const lastPrompt = `${player.name} to ${otherPlayer.name}:`; llmMessages.push({ role: 'user', content: lastPrompt }); const { content } = await chatCompletion({ messages: llmMessages, max_tokens: 300, stop: stopWords(otherPlayer.name, player.name), }); return trimContentPrefx(content, lastPrompt); } export async function leaveConversationMessage( ctx: ActionCtx, worldId: Id<'worlds'>, conversationId: GameId<'conversations'>, playerId: GameId<'players'>, otherPlayerId: GameId<'players'>, ): Promise { const { player, otherPlayer, conversation, agent, otherAgent } = await ctx.runQuery( selfInternal.queryPromptData, { worldId, playerId, otherPlayerId, conversationId, }, ); const prompt = [ `You are ${player.name}, and you're currently in a conversation with ${otherPlayer.name}.`, `You've decided to leave the question and would like to politely tell them you're leaving the conversation.`, ]; prompt.push(...agentPrompts(otherPlayer, agent, otherAgent ?? null)); prompt.push( `Below is the current chat history between you and ${otherPlayer.name}.`, `How would you like to tell them that you're leaving? Your response should be brief and within 200 characters.`, ); const llmMessages: LLMMessage[] = [ { role: 'system', content: prompt.join('\n'), }, ...(await previousMessages( ctx, worldId, player, otherPlayer, conversation.id as GameId<'conversations'>, )), ]; const lastPrompt = `${player.name} to ${otherPlayer.name}:`; llmMessages.push({ role: 'user', content: lastPrompt }); const { content } = await chatCompletion({ messages: llmMessages, max_tokens: 300, stop: stopWords(otherPlayer.name, player.name), }); return trimContentPrefx(content, lastPrompt); } function agentPrompts( otherPlayer: { name: string }, agent: { identity: string; plan: string } | null, otherAgent: { identity: string; plan: string } | null, ): string[] { const prompt = []; if (agent) { prompt.push(`About you: ${agent.identity}`); prompt.push(`Your goals for the conversation: ${agent.plan}`); } if (otherAgent) { prompt.push(`About ${otherPlayer.name}: ${otherAgent.identity}`); } return prompt; } function previousConversationPrompt( otherPlayer: { name: string }, conversation: { created: number } | null, ): string[] { const prompt = []; if (conversation) { const prev = new Date(conversation.created); const now = new Date(); prompt.push( `Last time you chatted with ${ otherPlayer.name } it was ${prev.toLocaleString()}. It's now ${now.toLocaleString()}.`, ); } return prompt; } function relatedMemoriesPrompt(memories: memory.Memory[]): string[] { const prompt = []; if (memories.length > 0) { prompt.push(`Here are some related memories in decreasing relevance order:`); for (const memory of memories) { prompt.push(' - ' + memory.description); } } return prompt; } async function previousMessages( ctx: ActionCtx, worldId: Id<'worlds'>, player: { id: string; name: string }, otherPlayer: { id: string; name: string }, conversationId: GameId<'conversations'>, ) { const llmMessages: LLMMessage[] = []; const prevMessages = await ctx.runQuery(api.messages.listMessages, { worldId, conversationId }); for (const message of prevMessages) { const author = message.author === player.id ? player : otherPlayer; const recipient = message.author === player.id ? otherPlayer : player; llmMessages.push({ role: 'user', content: `${author.name} to ${recipient.name}: ${message.text}`, }); } return llmMessages; } export const queryPromptData = internalQuery({ args: { worldId: v.id('worlds'), playerId, otherPlayerId: playerId, conversationId, }, handler: async (ctx, args) => { const world = await ctx.db.get(args.worldId); if (!world) { throw new Error(`World ${args.worldId} not found`); } const player = world.players.find((p) => p.id === args.playerId); if (!player) { throw new Error(`Player ${args.playerId} not found`); } const playerDescription = await ctx.db .query('playerDescriptions') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', args.playerId)) .first(); if (!playerDescription) { throw new Error(`Player description for ${args.playerId} not found`); } const otherPlayer = world.players.find((p) => p.id === args.otherPlayerId); if (!otherPlayer) { throw new Error(`Player ${args.otherPlayerId} not found`); } const otherPlayerDescription = await ctx.db .query('playerDescriptions') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', args.otherPlayerId)) .first(); if (!otherPlayerDescription) { throw new Error(`Player description for ${args.otherPlayerId} not found`); } const conversation = world.conversations.find((c) => c.id === args.conversationId); if (!conversation) { throw new Error(`Conversation ${args.conversationId} not found`); } const agent = world.agents.find((a) => a.playerId === args.playerId); if (!agent) { throw new Error(`Player ${args.playerId} not found`); } const agentDescription = await ctx.db .query('agentDescriptions') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('agentId', agent.id)) .first(); if (!agentDescription) { throw new Error(`Agent description for ${agent.id} not found`); } const otherAgent = world.agents.find((a) => a.playerId === args.otherPlayerId); let otherAgentDescription; if (otherAgent) { otherAgentDescription = await ctx.db .query('agentDescriptions') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('agentId', otherAgent.id)) .first(); if (!otherAgentDescription) { throw new Error(`Agent description for ${otherAgent.id} not found`); } } const lastTogether = await ctx.db .query('participatedTogether') .withIndex('edge', (q) => q .eq('worldId', args.worldId) .eq('player1', args.playerId) .eq('player2', args.otherPlayerId), ) // Order by conversation end time descending. .order('desc') .first(); let lastConversation = null; if (lastTogether) { lastConversation = await ctx.db .query('archivedConversations') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('id', lastTogether.conversationId), ) .first(); if (!lastConversation) { throw new Error(`Conversation ${lastTogether.conversationId} not found`); } } return { player: { name: playerDescription.name, ...player }, otherPlayer: { name: otherPlayerDescription.name, ...otherPlayer }, conversation, agent: { identity: agentDescription.identity, plan: agentDescription.plan, ...agent }, otherAgent: otherAgent && { identity: otherAgentDescription!.identity, plan: otherAgentDescription!.plan, ...otherAgent, }, lastConversation, }; }, }); function stopWords(otherPlayer: string, player: string) { // These are the words we ask the LLM to stop on. OpenAI only supports 4. const variants = [`${otherPlayer} to ${player}`]; return variants.flatMap((stop) => [stop + ':', stop.toLowerCase() + ':']); } ================================================ FILE: convex/agent/embeddingsCache.ts ================================================ import { v } from 'convex/values'; import { ActionCtx, internalMutation, internalQuery } from '../_generated/server'; import { internal } from '../_generated/api'; import { Id } from '../_generated/dataModel'; import { fetchEmbeddingBatch } from '../util/llm'; const selfInternal = internal.agent.embeddingsCache; export async function fetch(ctx: ActionCtx, text: string) { const result = await fetchBatch(ctx, [text]); return result.embeddings[0]; } export async function fetchBatch(ctx: ActionCtx, texts: string[]) { const start = Date.now(); const textHashes = await Promise.all(texts.map((text) => hashText(text))); const results = new Array(texts.length); const cacheResults = await ctx.runQuery(selfInternal.getEmbeddingsByText, { textHashes, }); for (const { index, embedding } of cacheResults) { results[index] = embedding; } const toWrite = []; if (cacheResults.length < texts.length) { const missingIndexes = [...results.keys()].filter((i) => !results[i]); const missingTexts = missingIndexes.map((i) => texts[i]); const response = await fetchEmbeddingBatch(missingTexts); if (response.embeddings.length !== missingIndexes.length) { throw new Error( `Expected ${missingIndexes.length} embeddings, got ${response.embeddings.length}`, ); } for (let i = 0; i < missingIndexes.length; i++) { const resultIndex = missingIndexes[i]; toWrite.push({ textHash: textHashes[resultIndex], embedding: response.embeddings[i], }); results[resultIndex] = response.embeddings[i]; } } if (toWrite.length > 0) { await ctx.runMutation(selfInternal.writeEmbeddings, { embeddings: toWrite }); } return { embeddings: results, hits: cacheResults.length, ms: Date.now() - start, }; } async function hashText(text: string) { const textEncoder = new TextEncoder(); const buf = textEncoder.encode(text); if (typeof crypto === 'undefined') { // Ugly, ugly hax to get ESBuild to not try to bundle this node dependency. const f = () => 'node:crypto'; const crypto = (await import(f())) as typeof import('crypto'); const hash = crypto.createHash('sha256'); hash.update(buf); return hash.digest().buffer; } else { return await crypto.subtle.digest('SHA-256', buf); } } export const getEmbeddingsByText = internalQuery({ args: { textHashes: v.array(v.bytes()) }, handler: async ( ctx, args, ): Promise<{ index: number; embeddingId: Id<'embeddingsCache'>; embedding: number[] }[]> => { const out = []; for (let i = 0; i < args.textHashes.length; i++) { const textHash = args.textHashes[i]; const result = await ctx.db .query('embeddingsCache') .withIndex('text', (q) => q.eq('textHash', textHash)) .first(); if (result) { out.push({ index: i, embeddingId: result._id, embedding: result.embedding, }); } } return out; }, }); export const writeEmbeddings = internalMutation({ args: { embeddings: v.array( v.object({ textHash: v.bytes(), embedding: v.array(v.float64()), }), ), }, handler: async (ctx, args): Promise[]> => { const ids = []; for (const embedding of args.embeddings) { ids.push(await ctx.db.insert('embeddingsCache', embedding)); } return ids; }, }); ================================================ FILE: convex/agent/memory.ts ================================================ import { v } from 'convex/values'; import { ActionCtx, DatabaseReader, internalMutation, internalQuery } from '../_generated/server'; import { Doc, Id } from '../_generated/dataModel'; import { internal } from '../_generated/api'; import { LLMMessage, chatCompletion, fetchEmbedding } from '../util/llm'; import { asyncMap } from '../util/asyncMap'; import { GameId, agentId, conversationId, playerId } from '../aiTown/ids'; import { SerializedPlayer } from '../aiTown/player'; import { memoryFields } from './schema'; // How long to wait before updating a memory's last access time. export const MEMORY_ACCESS_THROTTLE = 300_000; // In ms // We fetch 10x the number of memories by relevance, to have more candidates // for sorting by relevance + recency + importance. const MEMORY_OVERFETCH = 10; const selfInternal = internal.agent.memory; export type Memory = Doc<'memories'>; export type MemoryType = Memory['data']['type']; export type MemoryOfType = Omit & { data: Extract; }; export async function rememberConversation( ctx: ActionCtx, worldId: Id<'worlds'>, agentId: GameId<'agents'>, playerId: GameId<'players'>, conversationId: GameId<'conversations'>, ) { const data = await ctx.runQuery(selfInternal.loadConversation, { worldId, playerId, conversationId, }); const { player, otherPlayer } = data; const messages = await ctx.runQuery(selfInternal.loadMessages, { worldId, conversationId }); if (!messages.length) { return; } const llmMessages: LLMMessage[] = [ { role: 'user', content: `You are ${player.name}, and you just finished a conversation with ${otherPlayer.name}. I would like you to summarize the conversation from ${player.name}'s perspective, using first-person pronouns like "I," and add if you liked or disliked this interaction.`, }, ]; const authors = new Set>(); for (const message of messages) { const author = message.author === player.id ? player : otherPlayer; authors.add(author.id as GameId<'players'>); const recipient = message.author === player.id ? otherPlayer : player; llmMessages.push({ role: 'user', content: `${author.name} to ${recipient.name}: ${message.text}`, }); } llmMessages.push({ role: 'user', content: 'Summary:' }); const { content } = await chatCompletion({ messages: llmMessages, max_tokens: 500, }); const description = `Conversation with ${otherPlayer.name} at ${new Date( data.conversation._creationTime, ).toLocaleString()}: ${content}`; const importance = await calculateImportance(description); const { embedding } = await fetchEmbedding(description); authors.delete(player.id as GameId<'players'>); await ctx.runMutation(selfInternal.insertMemory, { agentId, playerId: player.id, description, importance, lastAccess: messages[messages.length - 1]._creationTime, data: { type: 'conversation', conversationId, playerIds: [...authors], }, embedding, }); await reflectOnMemories(ctx, worldId, playerId); return description; } export const loadConversation = internalQuery({ args: { worldId: v.id('worlds'), playerId, conversationId, }, handler: async (ctx, args) => { const world = await ctx.db.get(args.worldId); if (!world) { throw new Error(`World ${args.worldId} not found`); } const player = world.players.find((p) => p.id === args.playerId); if (!player) { throw new Error(`Player ${args.playerId} not found`); } const playerDescription = await ctx.db .query('playerDescriptions') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', args.playerId)) .first(); if (!playerDescription) { throw new Error(`Player description for ${args.playerId} not found`); } const conversation = await ctx.db .query('archivedConversations') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('id', args.conversationId)) .first(); if (!conversation) { throw new Error(`Conversation ${args.conversationId} not found`); } const otherParticipator = await ctx.db .query('participatedTogether') .withIndex('conversation', (q) => q .eq('worldId', args.worldId) .eq('player1', args.playerId) .eq('conversationId', args.conversationId), ) .first(); if (!otherParticipator) { throw new Error( `Couldn't find other participant in conversation ${args.conversationId} with player ${args.playerId}`, ); } const otherPlayerId = otherParticipator.player2; let otherPlayer: SerializedPlayer | Doc<'archivedPlayers'> | null = world.players.find((p) => p.id === otherPlayerId) ?? null; if (!otherPlayer) { otherPlayer = await ctx.db .query('archivedPlayers') .withIndex('worldId', (q) => q.eq('worldId', world._id).eq('id', otherPlayerId)) .first(); } if (!otherPlayer) { throw new Error(`Conversation ${args.conversationId} other player not found`); } const otherPlayerDescription = await ctx.db .query('playerDescriptions') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', otherPlayerId)) .first(); if (!otherPlayerDescription) { throw new Error(`Player description for ${otherPlayerId} not found`); } return { player: { ...player, name: playerDescription.name }, conversation, otherPlayer: { ...otherPlayer, name: otherPlayerDescription.name }, }; }, }); export async function searchMemories( ctx: ActionCtx, playerId: GameId<'players'>, searchEmbedding: number[], n: number = 3, ) { const candidates = await ctx.vectorSearch('memoryEmbeddings', 'embedding', { vector: searchEmbedding, filter: (q) => q.eq('playerId', playerId), limit: n * MEMORY_OVERFETCH, }); const rankedMemories = await ctx.runMutation(selfInternal.rankAndTouchMemories, { candidates, n, }); return rankedMemories.map(({ memory }) => memory); } function makeRange(values: number[]) { const min = Math.min(...values); const max = Math.max(...values); return [min, max] as const; } function normalize(value: number, range: readonly [number, number]) { const [min, max] = range; return (value - min) / (max - min); } export const rankAndTouchMemories = internalMutation({ args: { candidates: v.array(v.object({ _id: v.id('memoryEmbeddings'), _score: v.number() })), n: v.number(), }, handler: async (ctx, args) => { const ts = Date.now(); const relatedMemories = await asyncMap(args.candidates, async ({ _id }) => { const memory = await ctx.db .query('memories') .withIndex('embeddingId', (q) => q.eq('embeddingId', _id)) .first(); if (!memory) throw new Error(`Memory for embedding ${_id} not found`); return memory; }); // TODO: fetch recent memories and important memories // so we don't miss them in case they were a little less relevant. const recencyScore = relatedMemories.map((memory) => { const hoursSinceAccess = (ts - memory.lastAccess) / 1000 / 60 / 60; return 0.99 ** Math.floor(hoursSinceAccess); }); const relevanceRange = makeRange(args.candidates.map((c) => c._score)); const importanceRange = makeRange(relatedMemories.map((m) => m.importance)); const recencyRange = makeRange(recencyScore); const memoryScores = relatedMemories.map((memory, idx) => ({ memory, overallScore: normalize(args.candidates[idx]._score, relevanceRange) + normalize(memory.importance, importanceRange) + normalize(recencyScore[idx], recencyRange), })); memoryScores.sort((a, b) => b.overallScore - a.overallScore); const accessed = memoryScores.slice(0, args.n); await asyncMap(accessed, async ({ memory }) => { if (memory.lastAccess < ts - MEMORY_ACCESS_THROTTLE) { await ctx.db.patch(memory._id, { lastAccess: ts }); } }); return accessed; }, }); export const loadMessages = internalQuery({ args: { worldId: v.id('worlds'), conversationId, }, handler: async (ctx, args): Promise[]> => { const messages = await ctx.db .query('messages') .withIndex('conversationId', (q) => q.eq('worldId', args.worldId).eq('conversationId', args.conversationId), ) .collect(); return messages; }, }); async function calculateImportance(description: string) { const { content: importanceRaw } = await chatCompletion({ messages: [ { role: 'user', 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. Memory: ${description} Answer on a scale of 0 to 9. Respond with number only, e.g. "5"`, }, ], temperature: 0.0, max_tokens: 1, }); let importance = parseFloat(importanceRaw); if (isNaN(importance)) { importance = +(importanceRaw.match(/\d+/)?.[0] ?? NaN); } if (isNaN(importance)) { console.debug('Could not parse memory importance from: ', importanceRaw); importance = 5; } return importance; } const { embeddingId: _embeddingId, ...memoryFieldsWithoutEmbeddingId } = memoryFields; export const insertMemory = internalMutation({ args: { agentId, embedding: v.array(v.float64()), ...memoryFieldsWithoutEmbeddingId, }, handler: async (ctx, { agentId: _, embedding, ...memory }): Promise => { const embeddingId = await ctx.db.insert('memoryEmbeddings', { playerId: memory.playerId, embedding, }); await ctx.db.insert('memories', { ...memory, embeddingId, }); }, }); export const insertReflectionMemories = internalMutation({ args: { worldId: v.id('worlds'), playerId, reflections: v.array( v.object({ description: v.string(), relatedMemoryIds: v.array(v.id('memories')), importance: v.number(), embedding: v.array(v.float64()), }), ), }, handler: async (ctx, { playerId, reflections }) => { const lastAccess = Date.now(); for (const { embedding, relatedMemoryIds, ...rest } of reflections) { const embeddingId = await ctx.db.insert('memoryEmbeddings', { playerId, embedding, }); await ctx.db.insert('memories', { playerId, embeddingId, lastAccess, ...rest, data: { type: 'reflection', relatedMemoryIds, }, }); } }, }); async function reflectOnMemories( ctx: ActionCtx, worldId: Id<'worlds'>, playerId: GameId<'players'>, ) { const { memories, lastReflectionTs, name } = await ctx.runQuery( internal.agent.memory.getReflectionMemories, { worldId, playerId, numberOfItems: 100, }, ); // should only reflect if lastest 100 items have importance score of >500 const sumOfImportanceScore = memories .filter((m) => m._creationTime > (lastReflectionTs ?? 0)) .reduce((acc, curr) => acc + curr.importance, 0); const shouldReflect = sumOfImportanceScore > 500; if (!shouldReflect) { return false; } console.debug('sum of importance score = ', sumOfImportanceScore); console.debug('Reflecting...'); const prompt = ['[no prose]', '[Output only JSON]', `You are ${name}, statements about you:`]; memories.forEach((m, idx) => { prompt.push(`Statement ${idx}: ${m.description}`); }); prompt.push('What 3 high-level insights can you infer from the above statements?'); prompt.push( '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.', ); prompt.push( 'Example: [{insight: "...", statementIds: [1,2]}, {insight: "...", statementIds: [1]}, ...]', ); const { content: reflection } = await chatCompletion({ messages: [ { role: 'user', content: prompt.join('\n'), }, ], }); try { const insights = JSON.parse(reflection) as { insight: string; statementIds: number[] }[]; const memoriesToSave = await asyncMap(insights, async (item) => { const relatedMemoryIds = item.statementIds.map((idx: number) => memories[idx]._id); const importance = await calculateImportance(item.insight); const { embedding } = await fetchEmbedding(item.insight); console.debug('adding reflection memory...', item.insight); return { description: item.insight, embedding, importance, relatedMemoryIds, }; }); await ctx.runMutation(selfInternal.insertReflectionMemories, { worldId, playerId, reflections: memoriesToSave, }); } catch (e) { console.error('error saving or parsing reflection', e); console.debug('reflection', reflection); return false; } return true; } export const getReflectionMemories = internalQuery({ args: { worldId: v.id('worlds'), playerId, numberOfItems: v.number() }, handler: async (ctx, args) => { const world = await ctx.db.get(args.worldId); if (!world) { throw new Error(`World ${args.worldId} not found`); } const player = world.players.find((p) => p.id === args.playerId); if (!player) { throw new Error(`Player ${args.playerId} not found`); } const playerDescription = await ctx.db .query('playerDescriptions') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', args.playerId)) .first(); if (!playerDescription) { throw new Error(`Player description for ${args.playerId} not found`); } const memories = await ctx.db .query('memories') .withIndex('playerId', (q) => q.eq('playerId', player.id)) .order('desc') .take(args.numberOfItems); const lastReflection = await ctx.db .query('memories') .withIndex('playerId_type', (q) => q.eq('playerId', args.playerId).eq('data.type', 'reflection'), ) .order('desc') .first(); return { name: playerDescription.name, memories, lastReflectionTs: lastReflection?._creationTime, }; }, }); export async function latestMemoryOfType( db: DatabaseReader, playerId: GameId<'players'>, type: T, ) { const entry = await db .query('memories') .withIndex('playerId_type', (q) => q.eq('playerId', playerId).eq('data.type', type)) .order('desc') .first(); if (!entry) return null; return entry as MemoryOfType; } ================================================ FILE: convex/agent/schema.ts ================================================ import { v } from 'convex/values'; import { playerId, conversationId } from '../aiTown/ids'; import { defineTable } from 'convex/server'; import { EMBEDDING_DIMENSION } from '../util/llm'; export const memoryFields = { playerId, description: v.string(), embeddingId: v.id('memoryEmbeddings'), importance: v.number(), lastAccess: v.number(), data: v.union( // Setting up dynamics between players v.object({ type: v.literal('relationship'), // The player this memory is about, from the perspective of the player // whose memory this is. playerId, }), v.object({ type: v.literal('conversation'), conversationId, // The other player(s) in the conversation. playerIds: v.array(playerId), }), v.object({ type: v.literal('reflection'), relatedMemoryIds: v.array(v.id('memories')), }), ), }; export const memoryTables = { memories: defineTable(memoryFields) .index('embeddingId', ['embeddingId']) .index('playerId_type', ['playerId', 'data.type']) .index('playerId', ['playerId']), memoryEmbeddings: defineTable({ playerId, embedding: v.array(v.float64()), }).vectorIndex('embedding', { vectorField: 'embedding', filterFields: ['playerId'], dimensions: EMBEDDING_DIMENSION, }), }; export const agentTables = { ...memoryTables, embeddingsCache: defineTable({ textHash: v.bytes(), embedding: v.array(v.float64()), }).index('text', ['textHash']), }; ================================================ FILE: convex/aiTown/agent.ts ================================================ import { ObjectType, v } from 'convex/values'; import { GameId, parseGameId } from './ids'; import { agentId, conversationId, playerId } from './ids'; import { serializedPlayer } from './player'; import { Game } from './game'; import { ACTION_TIMEOUT, AWKWARD_CONVERSATION_TIMEOUT, CONVERSATION_COOLDOWN, CONVERSATION_DISTANCE, INVITE_ACCEPT_PROBABILITY, INVITE_TIMEOUT, MAX_CONVERSATION_DURATION, MAX_CONVERSATION_MESSAGES, MESSAGE_COOLDOWN, MIDPOINT_THRESHOLD, PLAYER_CONVERSATION_COOLDOWN, } from '../constants'; import { FunctionArgs } from 'convex/server'; import { MutationCtx, internalMutation, internalQuery } from '../_generated/server'; import { distance } from '../util/geometry'; import { internal } from '../_generated/api'; import { movePlayer } from './movement'; import { insertInput } from './insertInput'; export class Agent { id: GameId<'agents'>; playerId: GameId<'players'>; toRemember?: GameId<'conversations'>; lastConversation?: number; lastInviteAttempt?: number; inProgressOperation?: { name: string; operationId: string; started: number; }; constructor(serialized: SerializedAgent) { const { id, lastConversation, lastInviteAttempt, inProgressOperation } = serialized; const playerId = parseGameId('players', serialized.playerId); this.id = parseGameId('agents', id); this.playerId = playerId; this.toRemember = serialized.toRemember !== undefined ? parseGameId('conversations', serialized.toRemember) : undefined; this.lastConversation = lastConversation; this.lastInviteAttempt = lastInviteAttempt; this.inProgressOperation = inProgressOperation; } tick(game: Game, now: number) { const player = game.world.players.get(this.playerId); if (!player) { throw new Error(`Invalid player ID ${this.playerId}`); } if (this.inProgressOperation) { if (now < this.inProgressOperation.started + ACTION_TIMEOUT) { // Wait on the operation to finish. return; } console.log(`Timing out ${JSON.stringify(this.inProgressOperation)}`); delete this.inProgressOperation; } const conversation = game.world.playerConversation(player); const member = conversation?.participants.get(player.id); const recentlyAttemptedInvite = this.lastInviteAttempt && now < this.lastInviteAttempt + CONVERSATION_COOLDOWN; const doingActivity = player.activity && player.activity.until > now; if (doingActivity && (conversation || player.pathfinding)) { player.activity!.until = now; } // If we're not in a conversation, do something. // If we aren't doing an activity or moving, do something. // If we have been wandering but haven't thought about something to do for // a while, do something. if (!conversation && !doingActivity && (!player.pathfinding || !recentlyAttemptedInvite)) { this.startOperation(game, now, 'agentDoSomething', { worldId: game.worldId, player: player.serialize(), otherFreePlayers: [...game.world.players.values()] .filter((p) => p.id !== player.id) .filter( (p) => ![...game.world.conversations.values()].find((c) => c.participants.has(p.id)), ) .map((p) => p.serialize()), agent: this.serialize(), map: game.worldMap.serialize(), }); return; } // Check to see if we have a conversation we need to remember. if (this.toRemember) { // Fire off the action to remember the conversation. console.log(`Agent ${this.id} remembering conversation ${this.toRemember}`); this.startOperation(game, now, 'agentRememberConversation', { worldId: game.worldId, playerId: this.playerId, agentId: this.id, conversationId: this.toRemember, }); delete this.toRemember; return; } if (conversation && member) { const [otherPlayerId, otherMember] = [...conversation.participants.entries()].find( ([id]) => id !== player.id, )!; const otherPlayer = game.world.players.get(otherPlayerId)!; if (member.status.kind === 'invited') { // Accept a conversation with another agent with some probability and with // a human unconditionally. if (otherPlayer.human || Math.random() < INVITE_ACCEPT_PROBABILITY) { console.log(`Agent ${player.id} accepting invite from ${otherPlayer.id}`); conversation.acceptInvite(game, player); // Stop moving so we can start walking towards the other player. if (player.pathfinding) { delete player.pathfinding; } } else { console.log(`Agent ${player.id} rejecting invite from ${otherPlayer.id}`); conversation.rejectInvite(game, now, player); } return; } if (member.status.kind === 'walkingOver') { // Leave a conversation if we've been waiting for too long. if (member.invited + INVITE_TIMEOUT < now) { console.log(`Giving up on invite to ${otherPlayer.id}`); conversation.leave(game, now, player); return; } // Don't keep moving around if we're near enough. const playerDistance = distance(player.position, otherPlayer.position); if (playerDistance < CONVERSATION_DISTANCE) { return; } // Keep moving towards the other player. // If we're close enough to the player, just walk to them directly. if (!player.pathfinding) { let destination; if (playerDistance < MIDPOINT_THRESHOLD) { destination = { x: Math.floor(otherPlayer.position.x), y: Math.floor(otherPlayer.position.y), }; } else { destination = { x: Math.floor((player.position.x + otherPlayer.position.x) / 2), y: Math.floor((player.position.y + otherPlayer.position.y) / 2), }; } console.log(`Agent ${player.id} walking towards ${otherPlayer.id}...`, destination); movePlayer(game, now, player, destination); } return; } if (member.status.kind === 'participating') { const started = member.status.started; if (conversation.isTyping && conversation.isTyping.playerId !== player.id) { // Wait for the other player to finish typing. return; } if (!conversation.lastMessage) { const isInitiator = conversation.creator === player.id; const awkwardDeadline = started + AWKWARD_CONVERSATION_TIMEOUT; // Send the first message if we're the initiator or if we've been waiting for too long. if (isInitiator || awkwardDeadline < now) { // Grab the lock on the conversation and send a "start" message. console.log(`${player.id} initiating conversation with ${otherPlayer.id}.`); const messageUuid = crypto.randomUUID(); conversation.setIsTyping(now, player, messageUuid); this.startOperation(game, now, 'agentGenerateMessage', { worldId: game.worldId, playerId: player.id, agentId: this.id, conversationId: conversation.id, otherPlayerId: otherPlayer.id, messageUuid, type: 'start', }); return; } else { // Wait on the other player to say something up to the awkward deadline. return; } } // See if the conversation has been going on too long and decide to leave. const tooLongDeadline = started + MAX_CONVERSATION_DURATION; if (tooLongDeadline < now || conversation.numMessages > MAX_CONVERSATION_MESSAGES) { console.log(`${player.id} leaving conversation with ${otherPlayer.id}.`); const messageUuid = crypto.randomUUID(); conversation.setIsTyping(now, player, messageUuid); this.startOperation(game, now, 'agentGenerateMessage', { worldId: game.worldId, playerId: player.id, agentId: this.id, conversationId: conversation.id, otherPlayerId: otherPlayer.id, messageUuid, type: 'leave', }); return; } // Wait for the awkward deadline if we sent the last message. if (conversation.lastMessage.author === player.id) { const awkwardDeadline = conversation.lastMessage.timestamp + AWKWARD_CONVERSATION_TIMEOUT; if (now < awkwardDeadline) { return; } } // Wait for a cooldown after the last message to simulate "reading" the message. const messageCooldown = conversation.lastMessage.timestamp + MESSAGE_COOLDOWN; if (now < messageCooldown) { return; } // Grab the lock and send a message! console.log(`${player.id} continuing conversation with ${otherPlayer.id}.`); const messageUuid = crypto.randomUUID(); conversation.setIsTyping(now, player, messageUuid); this.startOperation(game, now, 'agentGenerateMessage', { worldId: game.worldId, playerId: player.id, agentId: this.id, conversationId: conversation.id, otherPlayerId: otherPlayer.id, messageUuid, type: 'continue', }); return; } } } startOperation( game: Game, now: number, name: Name, args: Omit, 'operationId'>, ) { if (this.inProgressOperation) { throw new Error( `Agent ${this.id} already has an operation: ${JSON.stringify(this.inProgressOperation)}`, ); } const operationId = game.allocId('operations'); console.log(`Agent ${this.id} starting operation ${name} (${operationId})`); game.scheduleOperation(name, { operationId, ...args } as any); this.inProgressOperation = { name, operationId, started: now, }; } serialize(): SerializedAgent { return { id: this.id, playerId: this.playerId, toRemember: this.toRemember, lastConversation: this.lastConversation, lastInviteAttempt: this.lastInviteAttempt, inProgressOperation: this.inProgressOperation, }; } } export const serializedAgent = { id: agentId, playerId: playerId, toRemember: v.optional(conversationId), lastConversation: v.optional(v.number()), lastInviteAttempt: v.optional(v.number()), inProgressOperation: v.optional( v.object({ name: v.string(), operationId: v.string(), started: v.number(), }), ), }; export type SerializedAgent = ObjectType; type AgentOperations = typeof internal.aiTown.agentOperations; export async function runAgentOperation(ctx: MutationCtx, operation: string, args: any) { let reference; switch (operation) { case 'agentRememberConversation': reference = internal.aiTown.agentOperations.agentRememberConversation; break; case 'agentGenerateMessage': reference = internal.aiTown.agentOperations.agentGenerateMessage; break; case 'agentDoSomething': reference = internal.aiTown.agentOperations.agentDoSomething; break; default: throw new Error(`Unknown operation: ${operation}`); } await ctx.scheduler.runAfter(0, reference, args); } export const agentSendMessage = internalMutation({ args: { worldId: v.id('worlds'), conversationId, agentId, playerId, text: v.string(), messageUuid: v.string(), leaveConversation: v.boolean(), operationId: v.string(), }, handler: async (ctx, args) => { await ctx.db.insert('messages', { conversationId: args.conversationId, author: args.playerId, text: args.text, messageUuid: args.messageUuid, worldId: args.worldId, }); await insertInput(ctx, args.worldId, 'agentFinishSendingMessage', { conversationId: args.conversationId, agentId: args.agentId, timestamp: Date.now(), leaveConversation: args.leaveConversation, operationId: args.operationId, }); }, }); export const findConversationCandidate = internalQuery({ args: { now: v.number(), worldId: v.id('worlds'), player: v.object(serializedPlayer), otherFreePlayers: v.array(v.object(serializedPlayer)), }, handler: async (ctx, { now, worldId, player, otherFreePlayers }) => { const { position } = player; const candidates = []; for (const otherPlayer of otherFreePlayers) { // Find the latest conversation we're both members of. const lastMember = await ctx.db .query('participatedTogether') .withIndex('edge', (q) => q.eq('worldId', worldId).eq('player1', player.id).eq('player2', otherPlayer.id), ) .order('desc') .first(); if (lastMember) { if (now < lastMember.ended + PLAYER_CONVERSATION_COOLDOWN) { continue; } } candidates.push({ id: otherPlayer.id, position }); } // Sort by distance and take the nearest candidate. candidates.sort((a, b) => distance(a.position, position) - distance(b.position, position)); return candidates[0]?.id; }, }); ================================================ FILE: convex/aiTown/agentDescription.ts ================================================ import { ObjectType, v } from 'convex/values'; import { GameId, agentId, parseGameId } from './ids'; export class AgentDescription { agentId: GameId<'agents'>; identity: string; plan: string; constructor(serialized: SerializedAgentDescription) { const { agentId, identity, plan } = serialized; this.agentId = parseGameId('agents', agentId); this.identity = identity; this.plan = plan; } serialize(): SerializedAgentDescription { const { agentId, identity, plan } = this; return { agentId, identity, plan }; } } export const serializedAgentDescription = { agentId, identity: v.string(), plan: v.string(), }; export type SerializedAgentDescription = ObjectType; ================================================ FILE: convex/aiTown/agentInputs.ts ================================================ import { v } from 'convex/values'; import { agentId, conversationId, parseGameId } from './ids'; import { Player, activity } from './player'; import { Conversation, conversationInputs } from './conversation'; import { movePlayer } from './movement'; import { inputHandler } from './inputHandler'; import { point } from '../util/types'; import { Descriptions } from '../../data/characters'; import { AgentDescription } from './agentDescription'; import { Agent } from './agent'; export const agentInputs = { finishRememberConversation: inputHandler({ args: { operationId: v.string(), agentId, }, handler: (game, now, args) => { const agentId = parseGameId('agents', args.agentId); const agent = game.world.agents.get(agentId); if (!agent) { throw new Error(`Couldn't find agent: ${agentId}`); } if ( !agent.inProgressOperation || agent.inProgressOperation.operationId !== args.operationId ) { console.debug(`Agent ${agentId} isn't remembering ${args.operationId}`); } else { delete agent.inProgressOperation; delete agent.toRemember; } return null; }, }), finishDoSomething: inputHandler({ args: { operationId: v.string(), agentId: v.id('agents'), destination: v.optional(point), invitee: v.optional(v.id('players')), activity: v.optional(activity), }, handler: (game, now, args) => { const agentId = parseGameId('agents', args.agentId); const agent = game.world.agents.get(agentId); if (!agent) { throw new Error(`Couldn't find agent: ${agentId}`); } if ( !agent.inProgressOperation || agent.inProgressOperation.operationId !== args.operationId ) { console.debug(`Agent ${agentId} didn't have ${args.operationId} in progress`); return null; } delete agent.inProgressOperation; const player = game.world.players.get(agent.playerId)!; if (args.invitee) { const inviteeId = parseGameId('players', args.invitee); const invitee = game.world.players.get(inviteeId); if (!invitee) { throw new Error(`Couldn't find player: ${inviteeId}`); } Conversation.start(game, now, player, invitee); agent.lastInviteAttempt = now; } if (args.destination) { movePlayer(game, now, player, args.destination); } if (args.activity) { player.activity = args.activity; } return null; }, }), agentFinishSendingMessage: inputHandler({ args: { agentId, conversationId, timestamp: v.number(), operationId: v.string(), leaveConversation: v.boolean(), }, handler: (game, now, args) => { const agentId = parseGameId('agents', args.agentId); const agent = game.world.agents.get(agentId); if (!agent) { throw new Error(`Couldn't find agent: ${agentId}`); } const player = game.world.players.get(agent.playerId); if (!player) { throw new Error(`Couldn't find player: ${agent.playerId}`); } const conversationId = parseGameId('conversations', args.conversationId); const conversation = game.world.conversations.get(conversationId); if (!conversation) { throw new Error(`Couldn't find conversation: ${conversationId}`); } if ( !agent.inProgressOperation || agent.inProgressOperation.operationId !== args.operationId ) { console.debug(`Agent ${agentId} wasn't sending a message ${args.operationId}`); return null; } delete agent.inProgressOperation; conversationInputs.finishSendingMessage.handler(game, now, { playerId: agent.playerId, conversationId: args.conversationId, timestamp: args.timestamp, }); if (args.leaveConversation) { conversation.leave(game, now, player); } return null; }, }), createAgent: inputHandler({ args: { descriptionIndex: v.number(), }, handler: (game, now, args) => { const description = Descriptions[args.descriptionIndex]; const playerId = Player.join( game, now, description.name, description.character, description.identity, ); const agentId = game.allocId('agents'); game.world.agents.set( agentId, new Agent({ id: agentId, playerId: playerId, inProgressOperation: undefined, lastConversation: undefined, lastInviteAttempt: undefined, toRemember: undefined, }), ); game.agentDescriptions.set( agentId, new AgentDescription({ agentId: agentId, identity: description.identity, plan: description.plan, }), ); return { agentId }; }, }), }; ================================================ FILE: convex/aiTown/agentOperations.ts ================================================ import { v } from 'convex/values'; import { internalAction } from '../_generated/server'; import { WorldMap, serializedWorldMap } from './worldMap'; import { rememberConversation } from '../agent/memory'; import { GameId, agentId, conversationId, playerId } from './ids'; import { continueConversationMessage, leaveConversationMessage, startConversationMessage, } from '../agent/conversation'; import { assertNever } from '../util/assertNever'; import { serializedAgent } from './agent'; import { ACTIVITIES, ACTIVITY_COOLDOWN, CONVERSATION_COOLDOWN } from '../constants'; import { api, internal } from '../_generated/api'; import { sleep } from '../util/sleep'; import { serializedPlayer } from './player'; export const agentRememberConversation = internalAction({ args: { worldId: v.id('worlds'), playerId, agentId, conversationId, operationId: v.string(), }, handler: async (ctx, args) => { await rememberConversation( ctx, args.worldId, args.agentId as GameId<'agents'>, args.playerId as GameId<'players'>, args.conversationId as GameId<'conversations'>, ); await sleep(Math.random() * 1000); await ctx.runMutation(api.aiTown.main.sendInput, { worldId: args.worldId, name: 'finishRememberConversation', args: { agentId: args.agentId, operationId: args.operationId, }, }); }, }); export const agentGenerateMessage = internalAction({ args: { worldId: v.id('worlds'), playerId, agentId, conversationId, otherPlayerId: playerId, operationId: v.string(), type: v.union(v.literal('start'), v.literal('continue'), v.literal('leave')), messageUuid: v.string(), }, handler: async (ctx, args) => { let completionFn; switch (args.type) { case 'start': completionFn = startConversationMessage; break; case 'continue': completionFn = continueConversationMessage; break; case 'leave': completionFn = leaveConversationMessage; break; default: assertNever(args.type); } const text = await completionFn( ctx, args.worldId, args.conversationId as GameId<'conversations'>, args.playerId as GameId<'players'>, args.otherPlayerId as GameId<'players'>, ); await ctx.runMutation(internal.aiTown.agent.agentSendMessage, { worldId: args.worldId, conversationId: args.conversationId, agentId: args.agentId, playerId: args.playerId, text, messageUuid: args.messageUuid, leaveConversation: args.type === 'leave', operationId: args.operationId, }); }, }); export const agentDoSomething = internalAction({ args: { worldId: v.id('worlds'), player: v.object(serializedPlayer), agent: v.object(serializedAgent), map: v.object(serializedWorldMap), otherFreePlayers: v.array(v.object(serializedPlayer)), operationId: v.string(), }, handler: async (ctx, args) => { const { player, agent } = args; const map = new WorldMap(args.map); const now = Date.now(); // Don't try to start a new conversation if we were just in one. const justLeftConversation = agent.lastConversation && now < agent.lastConversation + CONVERSATION_COOLDOWN; // Don't try again if we recently tried to find someone to invite. const recentlyAttemptedInvite = agent.lastInviteAttempt && now < agent.lastInviteAttempt + CONVERSATION_COOLDOWN; const recentActivity = player.activity && now < player.activity.until + ACTIVITY_COOLDOWN; // Decide whether to do an activity or wander somewhere. if (!player.pathfinding) { if (recentActivity || justLeftConversation) { await sleep(Math.random() * 1000); await ctx.runMutation(api.aiTown.main.sendInput, { worldId: args.worldId, name: 'finishDoSomething', args: { operationId: args.operationId, agentId: agent.id, destination: wanderDestination(map), }, }); return; } else { // TODO: have LLM choose the activity & emoji const activity = ACTIVITIES[Math.floor(Math.random() * ACTIVITIES.length)]; await sleep(Math.random() * 1000); await ctx.runMutation(api.aiTown.main.sendInput, { worldId: args.worldId, name: 'finishDoSomething', args: { operationId: args.operationId, agentId: agent.id, activity: { description: activity.description, emoji: activity.emoji, until: Date.now() + activity.duration, }, }, }); return; } } const invitee = justLeftConversation || recentlyAttemptedInvite ? undefined : await ctx.runQuery(internal.aiTown.agent.findConversationCandidate, { now, worldId: args.worldId, player: args.player, otherFreePlayers: args.otherFreePlayers, }); // TODO: We hit a lot of OCC errors on sending inputs in this file. It's // easy for them to get scheduled at the same time and line up in time. await sleep(Math.random() * 1000); await ctx.runMutation(api.aiTown.main.sendInput, { worldId: args.worldId, name: 'finishDoSomething', args: { operationId: args.operationId, agentId: args.agent.id, invitee, }, }); }, }); function wanderDestination(worldMap: WorldMap) { // Wander someonewhere at least one tile away from the edge. return { x: 1 + Math.floor(Math.random() * (worldMap.width - 2)), y: 1 + Math.floor(Math.random() * (worldMap.height - 2)), }; } ================================================ FILE: convex/aiTown/conversation.ts ================================================ import { ObjectType, v } from 'convex/values'; import { GameId, parseGameId } from './ids'; import { conversationId, playerId } from './ids'; import { Player } from './player'; import { inputHandler } from './inputHandler'; import { TYPING_TIMEOUT, CONVERSATION_DISTANCE } from '../constants'; import { distance, normalize, vector } from '../util/geometry'; import { Point } from '../util/types'; import { Game } from './game'; import { stopPlayer, blocked, movePlayer } from './movement'; import { ConversationMembership, serializedConversationMembership } from './conversationMembership'; import { parseMap, serializeMap } from '../util/object'; export class Conversation { id: GameId<'conversations'>; creator: GameId<'players'>; created: number; isTyping?: { playerId: GameId<'players'>; messageUuid: string; since: number; }; lastMessage?: { author: GameId<'players'>; timestamp: number; }; numMessages: number; participants: Map, ConversationMembership>; constructor(serialized: SerializedConversation) { const { id, creator, created, isTyping, lastMessage, numMessages, participants } = serialized; this.id = parseGameId('conversations', id); this.creator = parseGameId('players', creator); this.created = created; this.isTyping = isTyping && { playerId: parseGameId('players', isTyping.playerId), messageUuid: isTyping.messageUuid, since: isTyping.since, }; this.lastMessage = lastMessage && { author: parseGameId('players', lastMessage.author), timestamp: lastMessage.timestamp, }; this.numMessages = numMessages; this.participants = parseMap(participants, ConversationMembership, (m) => m.playerId); } tick(game: Game, now: number) { if (this.isTyping && this.isTyping.since + TYPING_TIMEOUT < now) { delete this.isTyping; } if (this.participants.size !== 2) { console.warn(`Conversation ${this.id} has ${this.participants.size} participants`); return; } const [playerId1, playerId2] = [...this.participants.keys()]; const member1 = this.participants.get(playerId1)!; const member2 = this.participants.get(playerId2)!; const player1 = game.world.players.get(playerId1)!; const player2 = game.world.players.get(playerId2)!; const playerDistance = distance(player1?.position, player2?.position); // If the players are both in the "walkingOver" state and they're sufficiently close, transition both // of them to "participating" and stop their paths. if (member1.status.kind === 'walkingOver' && member2.status.kind === 'walkingOver') { if (playerDistance < CONVERSATION_DISTANCE) { console.log(`Starting conversation between ${player1.id} and ${player2.id}`); // First, stop the two players from moving. stopPlayer(player1); stopPlayer(player2); member1.status = { kind: 'participating', started: now }; member2.status = { kind: 'participating', started: now }; // Try to move the first player to grid point nearest the other player. const neighbors = (p: Point) => [ { x: p.x + 1, y: p.y }, { x: p.x - 1, y: p.y }, { x: p.x, y: p.y + 1 }, { x: p.x, y: p.y - 1 }, ]; const floorPos1 = { x: Math.floor(player1.position.x), y: Math.floor(player1.position.y) }; const p1Candidates = neighbors(floorPos1).filter((p) => !blocked(game, now, p, player1.id)); p1Candidates.sort((a, b) => distance(a, player2.position) - distance(b, player2.position)); if (p1Candidates.length > 0) { const p1Candidate = p1Candidates[0]; // Try to move the second player to the grid point nearest the first player's // destination. const p2Candidates = neighbors(p1Candidate).filter( (p) => !blocked(game, now, p, player2.id), ); p2Candidates.sort( (a, b) => distance(a, player2.position) - distance(b, player2.position), ); if (p2Candidates.length > 0) { const p2Candidate = p2Candidates[0]; movePlayer(game, now, player1, p1Candidate, true); movePlayer(game, now, player2, p2Candidate, true); } } } } // Orient the two players towards each other if they're not moving. if (member1.status.kind === 'participating' && member2.status.kind === 'participating') { const v = normalize(vector(player1.position, player2.position)); if (!player1.pathfinding && v) { player1.facing = v; } if (!player2.pathfinding && v) { player2.facing.dx = -v.dx; player2.facing.dy = -v.dy; } } } static start(game: Game, now: number, player: Player, invitee: Player) { if (player.id === invitee.id) { throw new Error(`Can't invite yourself to a conversation`); } // Ensure the players still exist. if ([...game.world.conversations.values()].find((c) => c.participants.has(player.id))) { const reason = `Player ${player.id} is already in a conversation`; console.log(reason); return { error: reason }; } if ([...game.world.conversations.values()].find((c) => c.participants.has(invitee.id))) { const reason = `Player ${player.id} is already in a conversation`; console.log(reason); return { error: reason }; } const conversationId = game.allocId('conversations'); console.log(`Creating conversation ${conversationId}`); game.world.conversations.set( conversationId, new Conversation({ id: conversationId, created: now, creator: player.id, numMessages: 0, participants: [ { playerId: player.id, invited: now, status: { kind: 'walkingOver' } }, { playerId: invitee.id, invited: now, status: { kind: 'invited' } }, ], }), ); return { conversationId }; } setIsTyping(now: number, player: Player, messageUuid: string) { if (this.isTyping) { if (this.isTyping.playerId !== player.id) { throw new Error(`Player ${this.isTyping.playerId} is already typing in ${this.id}`); } return; } this.isTyping = { playerId: player.id, messageUuid, since: now }; } acceptInvite(game: Game, player: Player) { const member = this.participants.get(player.id); if (!member) { throw new Error(`Player ${player.id} not in conversation ${this.id}`); } if (member.status.kind !== 'invited') { throw new Error( `Invalid membership status for ${player.id}:${this.id}: ${JSON.stringify(member)}`, ); } member.status = { kind: 'walkingOver' }; } rejectInvite(game: Game, now: number, player: Player) { const member = this.participants.get(player.id); if (!member) { throw new Error(`Player ${player.id} not in conversation ${this.id}`); } if (member.status.kind !== 'invited') { throw new Error( `Rejecting invite in wrong membership state: ${this.id}:${player.id}: ${JSON.stringify( member, )}`, ); } this.stop(game, now); } stop(game: Game, now: number) { delete this.isTyping; for (const [playerId, member] of this.participants.entries()) { const agent = [...game.world.agents.values()].find((a) => a.playerId === playerId); if (agent) { agent.lastConversation = now; agent.toRemember = this.id; } } game.world.conversations.delete(this.id); } leave(game: Game, now: number, player: Player) { const member = this.participants.get(player.id); if (!member) { throw new Error(`Couldn't find membership for ${this.id}:${player.id}`); } this.stop(game, now); } serialize(): SerializedConversation { const { id, creator, created, isTyping, lastMessage, numMessages } = this; return { id, creator, created, isTyping, lastMessage, numMessages, participants: serializeMap(this.participants), }; } } export const serializedConversation = { id: conversationId, creator: playerId, created: v.number(), isTyping: v.optional( v.object({ playerId, messageUuid: v.string(), since: v.number(), }), ), lastMessage: v.optional( v.object({ author: playerId, timestamp: v.number(), }), ), numMessages: v.number(), participants: v.array(v.object(serializedConversationMembership)), }; export type SerializedConversation = ObjectType; export const conversationInputs = { // Start a conversation, inviting the specified player. // Conversations can only have two participants for now, // so we don't have a separate "invite" input. startConversation: inputHandler({ args: { playerId, invitee: playerId, }, handler: (game: Game, now: number, args): GameId<'conversations'> => { const playerId = parseGameId('players', args.playerId); const player = game.world.players.get(playerId); if (!player) { throw new Error(`Invalid player ID: ${playerId}`); } const inviteeId = parseGameId('players', args.invitee); const invitee = game.world.players.get(inviteeId); if (!invitee) { throw new Error(`Invalid player ID: ${inviteeId}`); } console.log(`Starting ${playerId} ${inviteeId}...`); const { conversationId, error } = Conversation.start(game, now, player, invitee); if (!conversationId) { // TODO: pass it back to the client for them to show an error. throw new Error(error); } return conversationId; }, }), startTyping: inputHandler({ args: { playerId, conversationId, messageUuid: v.string(), }, handler: (game: Game, now: number, args): null => { const playerId = parseGameId('players', args.playerId); const player = game.world.players.get(playerId); if (!player) { throw new Error(`Invalid player ID: ${playerId}`); } const conversationId = parseGameId('conversations', args.conversationId); const conversation = game.world.conversations.get(conversationId); if (!conversation) { throw new Error(`Invalid conversation ID: ${conversationId}`); } if (conversation.isTyping && conversation.isTyping.playerId !== playerId) { throw new Error( `Player ${conversation.isTyping.playerId} is already typing in ${conversationId}`, ); } conversation.isTyping = { playerId, messageUuid: args.messageUuid, since: now }; return null; }, }), finishSendingMessage: inputHandler({ args: { playerId, conversationId, timestamp: v.number(), }, handler: (game: Game, now: number, args): null => { const playerId = parseGameId('players', args.playerId); const conversationId = parseGameId('conversations', args.conversationId); const conversation = game.world.conversations.get(conversationId); if (!conversation) { throw new Error(`Invalid conversation ID: ${conversationId}`); } if (conversation.isTyping && conversation.isTyping.playerId === playerId) { delete conversation.isTyping; } conversation.lastMessage = { author: playerId, timestamp: args.timestamp }; conversation.numMessages++; return null; }, }), // Accept an invite to a conversation, which puts the // player in the "walkingOver" state until they're close // enough to the other participant. acceptInvite: inputHandler({ args: { playerId, conversationId, }, handler: (game: Game, now: number, args): null => { const playerId = parseGameId('players', args.playerId); const player = game.world.players.get(playerId); if (!player) { throw new Error(`Invalid player ID ${playerId}`); } const conversationId = parseGameId('conversations', args.conversationId); const conversation = game.world.conversations.get(conversationId); if (!conversation) { throw new Error(`Invalid conversation ID ${conversationId}`); } conversation.acceptInvite(game, player); return null; }, }), // Reject the invite. Eventually we might add a message // that explains why! rejectInvite: inputHandler({ args: { playerId, conversationId, }, handler: (game: Game, now: number, args): null => { const playerId = parseGameId('players', args.playerId); const player = game.world.players.get(playerId); if (!player) { throw new Error(`Invalid player ID ${playerId}`); } const conversationId = parseGameId('conversations', args.conversationId); const conversation = game.world.conversations.get(conversationId); if (!conversation) { throw new Error(`Invalid conversation ID ${conversationId}`); } conversation.rejectInvite(game, now, player); return null; }, }), // Leave a conversation. leaveConversation: inputHandler({ args: { playerId, conversationId, }, handler: (game: Game, now: number, args): null => { const playerId = parseGameId('players', args.playerId); const player = game.world.players.get(playerId); if (!player) { throw new Error(`Invalid player ID ${playerId}`); } const conversationId = parseGameId('conversations', args.conversationId); const conversation = game.world.conversations.get(conversationId); if (!conversation) { throw new Error(`Invalid conversation ID ${conversationId}`); } conversation.leave(game, now, player); return null; }, }), }; ================================================ FILE: convex/aiTown/conversationMembership.ts ================================================ import { ObjectType, v } from 'convex/values'; import { GameId, parseGameId, playerId } from './ids'; export const serializedConversationMembership = { playerId, invited: v.number(), status: v.union( v.object({ kind: v.literal('invited') }), v.object({ kind: v.literal('walkingOver') }), v.object({ kind: v.literal('participating'), started: v.number() }), ), }; export type SerializedConversationMembership = ObjectType; export class ConversationMembership { playerId: GameId<'players'>; invited: number; status: | { kind: 'invited' } | { kind: 'walkingOver' } | { kind: 'participating'; started: number }; constructor(serialized: SerializedConversationMembership) { const { playerId, invited, status } = serialized; this.playerId = parseGameId('players', playerId); this.invited = invited; this.status = status; } serialize(): SerializedConversationMembership { const { playerId, invited, status } = this; return { playerId, invited, status, }; } } ================================================ FILE: convex/aiTown/game.ts ================================================ import { Infer, v } from 'convex/values'; import { Doc, Id } from '../_generated/dataModel'; import { ActionCtx, DatabaseReader, MutationCtx, internalMutation, internalQuery, } from '../_generated/server'; import { World, serializedWorld } from './world'; import { WorldMap, serializedWorldMap } from './worldMap'; import { PlayerDescription, serializedPlayerDescription } from './playerDescription'; import { Location, locationFields, playerLocation } from './location'; import { runAgentOperation } from './agent'; import { GameId, IdTypes, allocGameId } from './ids'; import { InputArgs, InputNames, inputs } from './inputs'; import { AbstractGame, EngineUpdate, applyEngineUpdate, engineUpdate, loadEngine, } from '../engine/abstractGame'; import { internal } from '../_generated/api'; import { HistoricalObject } from '../engine/historicalObject'; import { AgentDescription, serializedAgentDescription } from './agentDescription'; import { parseMap, serializeMap } from '../util/object'; const gameState = v.object({ world: v.object(serializedWorld), playerDescriptions: v.array(v.object(serializedPlayerDescription)), agentDescriptions: v.array(v.object(serializedAgentDescription)), worldMap: v.object(serializedWorldMap), }); type GameState = Infer; const gameStateDiff = v.object({ world: v.object(serializedWorld), playerDescriptions: v.optional(v.array(v.object(serializedPlayerDescription))), agentDescriptions: v.optional(v.array(v.object(serializedAgentDescription))), worldMap: v.optional(v.object(serializedWorldMap)), agentOperations: v.array(v.object({ name: v.string(), args: v.any() })), }); type GameStateDiff = Infer; export class Game extends AbstractGame { tickDuration = 16; stepDuration = 1000; maxTicksPerStep = 600; maxInputsPerStep = 32; world: World; historicalLocations: Map, HistoricalObject>; descriptionsModified: boolean; worldMap: WorldMap; playerDescriptions: Map, PlayerDescription>; agentDescriptions: Map, AgentDescription>; pendingOperations: Array<{ name: string; args: any }> = []; numPathfinds: number; constructor( engine: Doc<'engines'>, public worldId: Id<'worlds'>, state: GameState, ) { super(engine); this.world = new World(state.world); delete this.world.historicalLocations; this.descriptionsModified = false; this.worldMap = new WorldMap(state.worldMap); this.agentDescriptions = parseMap(state.agentDescriptions, AgentDescription, (a) => a.agentId); this.playerDescriptions = parseMap( state.playerDescriptions, PlayerDescription, (p) => p.playerId, ); this.historicalLocations = new Map(); this.numPathfinds = 0; } static async load( db: DatabaseReader, worldId: Id<'worlds'>, generationNumber: number, ): Promise<{ engine: Doc<'engines'>; gameState: GameState }> { const worldDoc = await db.get(worldId); if (!worldDoc) { throw new Error(`No world found with id ${worldId}`); } const worldStatus = await db .query('worldStatus') .withIndex('worldId', (q) => q.eq('worldId', worldId)) .unique(); if (!worldStatus) { throw new Error(`No engine found for world ${worldId}`); } const engine = await loadEngine(db, worldStatus.engineId, generationNumber); const playerDescriptionsDocs = await db .query('playerDescriptions') .withIndex('worldId', (q) => q.eq('worldId', worldId)) .collect(); const agentDescriptionsDocs = await db .query('agentDescriptions') .withIndex('worldId', (q) => q.eq('worldId', worldId)) .collect(); const worldMapDoc = await db .query('maps') .withIndex('worldId', (q) => q.eq('worldId', worldId)) .unique(); if (!worldMapDoc) { throw new Error(`No map found for world ${worldId}`); } // Discard the system fields and historicalLocations from the world state. const { _id, _creationTime, historicalLocations: _, ...world } = worldDoc; const playerDescriptions = playerDescriptionsDocs // Discard player descriptions for players that no longer exist. .filter((d) => !!world.players.find((p) => p.id === d.playerId)) .map(({ _id, _creationTime, worldId: _, ...doc }) => doc); const agentDescriptions = agentDescriptionsDocs .filter((a) => !!world.agents.find((p) => p.id === a.agentId)) .map(({ _id, _creationTime, worldId: _, ...doc }) => doc); const { _id: _mapId, _creationTime: _mapCreationTime, worldId: _mapWorldId, ...worldMap } = worldMapDoc; return { engine, gameState: { world, playerDescriptions, agentDescriptions, worldMap, }, }; } allocId(idType: T): GameId { const id = allocGameId(idType, this.world.nextId); this.world.nextId += 1; return id; } scheduleOperation(name: string, args: unknown) { this.pendingOperations.push({ name, args }); } handleInput(now: number, name: Name, args: InputArgs) { const handler = inputs[name]?.handler; if (!handler) { throw new Error(`Invalid input: ${name}`); } return handler(this, now, args as any); } beginStep(_now: number) { // Store the current location of all players in the history tracking buffer. this.historicalLocations.clear(); for (const player of this.world.players.values()) { this.historicalLocations.set( player.id, new HistoricalObject(locationFields, playerLocation(player)), ); } this.numPathfinds = 0; } tick(now: number) { for (const player of this.world.players.values()) { player.tick(this, now); } for (const player of this.world.players.values()) { player.tickPathfinding(this, now); } for (const player of this.world.players.values()) { player.tickPosition(this, now); } for (const conversation of this.world.conversations.values()) { conversation.tick(this, now); } for (const agent of this.world.agents.values()) { agent.tick(this, now); } // Save each player's location into the history buffer at the end of // each tick. for (const player of this.world.players.values()) { let historicalObject = this.historicalLocations.get(player.id); if (!historicalObject) { historicalObject = new HistoricalObject(locationFields, playerLocation(player)); this.historicalLocations.set(player.id, historicalObject); } historicalObject.update(now, playerLocation(player)); } } async saveStep(ctx: ActionCtx, engineUpdate: EngineUpdate): Promise { const diff = this.takeDiff(); await ctx.runMutation(internal.aiTown.game.saveWorld, { engineId: this.engine._id, engineUpdate, worldId: this.worldId, worldDiff: diff, }); } takeDiff(): GameStateDiff { const historicalLocations = []; let bufferSize = 0; for (const [id, historicalObject] of this.historicalLocations.entries()) { const buffer = historicalObject.pack(); if (!buffer) { continue; } historicalLocations.push({ playerId: id, location: buffer }); bufferSize += buffer.byteLength; } if (bufferSize > 0) { console.debug( `Packed ${Object.entries(historicalLocations).length} history buffers in ${( bufferSize / 1024 ).toFixed(2)}KiB.`, ); } this.historicalLocations.clear(); const result: GameStateDiff = { world: { ...this.world.serialize(), historicalLocations }, agentOperations: this.pendingOperations, }; this.pendingOperations = []; if (this.descriptionsModified) { result.playerDescriptions = serializeMap(this.playerDescriptions); result.agentDescriptions = serializeMap(this.agentDescriptions); result.worldMap = this.worldMap.serialize(); this.descriptionsModified = false; } return result; } static async saveDiff(ctx: MutationCtx, worldId: Id<'worlds'>, diff: GameStateDiff) { const existingWorld = await ctx.db.get(worldId); if (!existingWorld) { throw new Error(`No world found with id ${worldId}`); } const newWorld = diff.world; // Archive newly deleted players, conversations, and agents. for (const player of existingWorld.players) { if (!newWorld.players.some((p) => p.id === player.id)) { await ctx.db.insert('archivedPlayers', { worldId, ...player }); } } for (const conversation of existingWorld.conversations) { if (!newWorld.conversations.some((c) => c.id === conversation.id)) { const participants = conversation.participants.map((p) => p.playerId); const archivedConversation = { worldId, id: conversation.id, created: conversation.created, creator: conversation.creator, ended: Date.now(), lastMessage: conversation.lastMessage, numMessages: conversation.numMessages, participants, }; await ctx.db.insert('archivedConversations', archivedConversation); for (let i = 0; i < participants.length; i++) { for (let j = 0; j < participants.length; j++) { if (i == j) { continue; } const player1 = participants[i]; const player2 = participants[j]; await ctx.db.insert('participatedTogether', { worldId, conversationId: conversation.id, player1, player2, ended: Date.now(), }); } } } } for (const conversation of existingWorld.agents) { if (!newWorld.agents.some((a) => a.id === conversation.id)) { await ctx.db.insert('archivedAgents', { worldId, ...conversation }); } } // Update the world state. await ctx.db.replace(worldId, newWorld); // Update the larger description tables if they changed. const { playerDescriptions, agentDescriptions, worldMap } = diff; if (playerDescriptions) { for (const description of playerDescriptions) { const existing = await ctx.db .query('playerDescriptions') .withIndex('worldId', (q) => q.eq('worldId', worldId).eq('playerId', description.playerId), ) .unique(); if (existing) { await ctx.db.replace(existing._id, { worldId, ...description }); } else { await ctx.db.insert('playerDescriptions', { worldId, ...description }); } } } if (agentDescriptions) { for (const description of agentDescriptions) { const existing = await ctx.db .query('agentDescriptions') .withIndex('worldId', (q) => q.eq('worldId', worldId).eq('agentId', description.agentId)) .unique(); if (existing) { await ctx.db.replace(existing._id, { worldId, ...description }); } else { await ctx.db.insert('agentDescriptions', { worldId, ...description }); } } } if (worldMap) { const existing = await ctx.db .query('maps') .withIndex('worldId', (q) => q.eq('worldId', worldId)) .unique(); if (existing) { await ctx.db.replace(existing._id, { worldId, ...worldMap }); } else { await ctx.db.insert('maps', { worldId, ...worldMap }); } } // Start the desired agent operations. for (const operation of diff.agentOperations) { await runAgentOperation(ctx, operation.name, operation.args); } } } export const loadWorld = internalQuery({ args: { worldId: v.id('worlds'), generationNumber: v.number(), }, handler: async (ctx, args) => { return await Game.load(ctx.db, args.worldId, args.generationNumber); }, }); export const saveWorld = internalMutation({ args: { engineId: v.id('engines'), engineUpdate, worldId: v.id('worlds'), worldDiff: gameStateDiff, }, handler: async (ctx, args) => { await applyEngineUpdate(ctx, args.engineId, args.engineUpdate); await Game.saveDiff(ctx, args.worldId, args.worldDiff); }, }); ================================================ FILE: convex/aiTown/ids.ts ================================================ import { v } from 'convex/values'; const IdShortCodes = { agents: 'a', conversations: 'c', players: 'p', operations: 'o' }; export type IdTypes = keyof typeof IdShortCodes; export type GameId = string & { __type: T }; export function parseGameId(idType: T, gameId: string): GameId { const type = gameId[0]; const match = Object.entries(IdShortCodes).find(([_, value]) => value === type); if (!match || match[0] !== idType) { throw new Error(`Invalid game ID type: ${type}`); } const number = parseInt(gameId.slice(2), 10); if (isNaN(number) || !Number.isInteger(number) || number < 0) { throw new Error(`Invalid game ID number: ${gameId}`); } return gameId as GameId; } export function allocGameId(idType: T, idNumber: number): GameId { const type = IdShortCodes[idType]; if (!type) { throw new Error(`Invalid game ID type: ${idType}`); } return `${type}:${idNumber}` as GameId; } export const conversationId = v.string(); export const playerId = v.string(); export const agentId = v.string(); export const operationId = v.string(); ================================================ FILE: convex/aiTown/inputHandler.ts ================================================ import { ObjectType, PropertyValidators, Value } from 'convex/values'; import type { Game } from './game'; export function inputHandler(def: { args: ArgsValidator; handler: (game: Game, now: number, args: ObjectType) => Return; }) { return def; } ================================================ FILE: convex/aiTown/inputs.ts ================================================ import { ObjectType } from 'convex/values'; import { playerInputs } from './player'; import { conversationInputs } from './conversation'; import { agentInputs } from './agentInputs'; // It's easy to hit circular dependencies with these imports, // so assert at module scope so we hit errors when analyzing. if (playerInputs === undefined || conversationInputs === undefined || agentInputs === undefined) { throw new Error("Input map is undefined, check if there's a circular import."); } export const inputs = { ...playerInputs, // Inputs for the messaging layer. ...conversationInputs, // Inputs for the agent layer. ...agentInputs, }; export type Inputs = typeof inputs; export type InputNames = keyof Inputs; export type InputArgs = ObjectType; export type InputReturnValue = ReturnType< Inputs[Name]['handler'] > extends Promise ? T : never; ================================================ FILE: convex/aiTown/insertInput.ts ================================================ import { MutationCtx } from '../_generated/server'; import { Id } from '../_generated/dataModel'; import { engineInsertInput } from '../engine/abstractGame'; import { InputNames, InputArgs } from './inputs'; export async function insertInput( ctx: MutationCtx, worldId: Id<'worlds'>, name: Name, args: InputArgs, ): Promise> { const worldStatus = await ctx.db .query('worldStatus') .withIndex('worldId', (q) => q.eq('worldId', worldId)) .unique(); if (!worldStatus) { throw new Error(`World for engine ${worldId} not found`); } return await engineInsertInput(ctx, worldStatus.engineId, name, args); } ================================================ FILE: convex/aiTown/location.ts ================================================ import { FieldConfig } from '../engine/historicalObject'; import { Player } from './player'; export type Location = { // Unpacked player position. x: number; y: number; // Normalized facing vector. dx: number; dy: number; speed: number; }; export const locationFields: FieldConfig = [ { name: 'x', precision: 8 }, { name: 'y', precision: 8 }, { name: 'dx', precision: 8 }, { name: 'dy', precision: 8 }, { name: 'speed', precision: 16 }, ]; export function playerLocation(player: Player): Location { return { x: player.position.x, y: player.position.y, dx: player.facing.dx, dy: player.facing.dy, speed: player.speed, }; } ================================================ FILE: convex/aiTown/main.ts ================================================ import { ConvexError, v } from 'convex/values'; import { DatabaseReader, MutationCtx, internalAction, mutation, query } from '../_generated/server'; import { insertInput } from './insertInput'; import { Game } from './game'; import { internal } from '../_generated/api'; import { sleep } from '../util/sleep'; import { Id } from '../_generated/dataModel'; import { ENGINE_ACTION_DURATION } from '../constants'; export async function createEngine(ctx: MutationCtx) { const now = Date.now(); const engineId = await ctx.db.insert('engines', { currentTime: now, generationNumber: 0, running: true, }); return engineId; } async function loadWorldStatus(db: DatabaseReader, worldId: Id<'worlds'>) { const worldStatus = await db .query('worldStatus') .withIndex('worldId', (q) => q.eq('worldId', worldId)) .unique(); if (!worldStatus) { throw new Error(`No engine found for world ${worldId}`); } return worldStatus; } export async function startEngine(ctx: MutationCtx, worldId: Id<'worlds'>) { const { engineId } = await loadWorldStatus(ctx.db, worldId); const engine = await ctx.db.get(engineId); if (!engine) { throw new Error(`Invalid engine ID: ${engineId}`); } if (engine.running) { throw new Error(`Engine ${engineId} isn't currently stopped`); } const now = Date.now(); const generationNumber = engine.generationNumber + 1; await ctx.db.patch(engineId, { // Forcibly advance time to the present. This does mean we'll skip // simulating the time the engine was stopped, but we don't want // to have to simulate a potentially large stopped window and send // it down to clients. lastStepTs: engine.currentTime, currentTime: now, running: true, generationNumber, }); await ctx.scheduler.runAfter(0, internal.aiTown.main.runStep, { worldId: worldId, generationNumber, maxDuration: ENGINE_ACTION_DURATION, }); } export async function kickEngine(ctx: MutationCtx, worldId: Id<'worlds'>) { const { engineId } = await loadWorldStatus(ctx.db, worldId); const engine = await ctx.db.get(engineId); if (!engine) { throw new Error(`Invalid engine ID: ${engineId}`); } if (!engine.running) { throw new Error(`Engine ${engineId} isn't currently running`); } const generationNumber = engine.generationNumber + 1; await ctx.db.patch(engineId, { generationNumber }); await ctx.scheduler.runAfter(0, internal.aiTown.main.runStep, { worldId: worldId, generationNumber, maxDuration: ENGINE_ACTION_DURATION, }); } export async function stopEngine(ctx: MutationCtx, worldId: Id<'worlds'>) { const { engineId } = await loadWorldStatus(ctx.db, worldId); const engine = await ctx.db.get(engineId); if (!engine) { throw new Error(`Invalid engine ID: ${engineId}`); } if (!engine.running) { throw new Error(`Engine ${engineId} isn't currently running`); } await ctx.db.patch(engineId, { running: false }); } export const runStep = internalAction({ args: { worldId: v.id('worlds'), generationNumber: v.number(), maxDuration: v.number(), }, handler: async (ctx, args) => { try { const { engine, gameState } = await ctx.runQuery(internal.aiTown.game.loadWorld, { worldId: args.worldId, generationNumber: args.generationNumber, }); const game = new Game(engine, args.worldId, gameState); let now = Date.now(); const deadline = now + args.maxDuration; while (now < deadline) { await game.runStep(ctx, now); const sleepUntil = Math.min(now + game.stepDuration, deadline); await sleep(sleepUntil - now); now = Date.now(); } await ctx.scheduler.runAfter(0, internal.aiTown.main.runStep, { worldId: args.worldId, generationNumber: game.engine.generationNumber, maxDuration: args.maxDuration, }); } catch (e: unknown) { if (e instanceof ConvexError) { if (e.data.kind === 'engineNotRunning') { console.debug(`Engine is not running: ${e.message}`); return; } if (e.data.kind === 'generationNumber') { console.debug(`Generation number mismatch: ${e.message}`); return; } } throw e; } }, }); export const sendInput = mutation({ args: { worldId: v.id('worlds'), name: v.string(), args: v.any(), }, handler: async (ctx, args) => { return await insertInput(ctx, args.worldId, args.name as any, args.args); }, }); export const inputStatus = query({ args: { inputId: v.id('inputs'), }, handler: async (ctx, args) => { const input = await ctx.db.get(args.inputId); if (!input) { throw new Error(`Invalid input ID: ${args.inputId}`); } return input.returnValue ?? null; }, }); ================================================ FILE: convex/aiTown/movement.ts ================================================ import { movementSpeed } from '../../data/characters'; import { COLLISION_THRESHOLD } from '../constants'; import { compressPath, distance, manhattanDistance, pointsEqual } from '../util/geometry'; import { MinHeap } from '../util/minheap'; import { Point, Vector } from '../util/types'; import { Game } from './game'; import { GameId } from './ids'; import { Player } from './player'; import { WorldMap } from './worldMap'; type PathCandidate = { position: Point; facing?: Vector; t: number; length: number; cost: number; prev?: PathCandidate; }; export function stopPlayer(player: Player) { delete player.pathfinding; player.speed = 0; } export function movePlayer( game: Game, now: number, player: Player, destination: Point, allowInConversation?: boolean, ) { if (Math.floor(destination.x) !== destination.x || Math.floor(destination.y) !== destination.y) { throw new Error(`Non-integral destination: ${JSON.stringify(destination)}`); } const { position } = player; // Close enough to current position or destination => no-op. if (pointsEqual(position, destination)) { return; } // Don't allow players in a conversation to move. const inConversation = [...game.world.conversations.values()].some( (c) => c.participants.get(player.id)?.status.kind === 'participating', ); if (inConversation && !allowInConversation) { throw new Error(`Can't move when in a conversation. Leave the conversation first!`); } player.pathfinding = { destination: destination, started: now, state: { kind: 'needsPath', }, }; return; } export function findRoute(game: Game, now: number, player: Player, destination: Point) { const minDistances: PathCandidate[][] = []; const explore = (current: PathCandidate): Array => { const { x, y } = current.position; const neighbors = []; // If we're not on a grid point, first try to move horizontally // or vertically to a grid point. Note that this can create very small // deltas between the current position and the nearest grid point so // be careful to preserve the `facing` vectors rather than trying to // derive them anew. if (x !== Math.floor(x)) { neighbors.push( { position: { x: Math.floor(x), y }, facing: { dx: -1, dy: 0 } }, { position: { x: Math.floor(x) + 1, y }, facing: { dx: 1, dy: 0 } }, ); } if (y !== Math.floor(y)) { neighbors.push( { position: { x, y: Math.floor(y) }, facing: { dx: 0, dy: -1 } }, { position: { x, y: Math.floor(y) + 1 }, facing: { dx: 0, dy: 1 } }, ); } // Otherwise, just move to adjacent grid points. if (x == Math.floor(x) && y == Math.floor(y)) { neighbors.push( { position: { x: x + 1, y }, facing: { dx: 1, dy: 0 } }, { position: { x: x - 1, y }, facing: { dx: -1, dy: 0 } }, { position: { x, y: y + 1 }, facing: { dx: 0, dy: 1 } }, { position: { x, y: y - 1 }, facing: { dx: 0, dy: -1 } }, ); } const next = []; for (const { position, facing } of neighbors) { const segmentLength = distance(current.position, position); const length = current.length + segmentLength; if (blocked(game, now, position, player.id)) { continue; } const remaining = manhattanDistance(position, destination); const path = { position, facing, // Movement speed is in tiles per second. t: current.t + (segmentLength / movementSpeed) * 1000, length, cost: length + remaining, prev: current, }; const existingMin = minDistances[position.y]?.[position.x]; if (existingMin && existingMin.cost <= path.cost) { continue; } minDistances[position.y] ??= []; minDistances[position.y][position.x] = path; next.push(path); } return next; }; const startingLocation = player.position; const startingPosition = { x: startingLocation.x, y: startingLocation.y }; let current: PathCandidate | undefined = { position: startingPosition, facing: player.facing, t: now, length: 0, cost: manhattanDistance(startingPosition, destination), prev: undefined, }; let bestCandidate = current; const minheap = MinHeap((p0, p1) => p0.cost > p1.cost); while (current) { if (pointsEqual(current.position, destination)) { break; } if ( manhattanDistance(current.position, destination) < manhattanDistance(bestCandidate.position, destination) ) { bestCandidate = current; } for (const candidate of explore(current)) { minheap.push(candidate); } current = minheap.pop(); } let newDestination = null; if (!current) { if (bestCandidate.length === 0) { return null; } current = bestCandidate; newDestination = current.position; } const densePath = []; let facing = current.facing!; while (current) { densePath.push({ position: current.position, t: current.t, facing }); facing = current.facing!; current = current.prev; } densePath.reverse(); return { path: compressPath(densePath), newDestination }; } export function blocked(game: Game, now: number, pos: Point, playerId?: GameId<'players'>) { const otherPositions = [...game.world.players.values()] .filter((p) => p.id !== playerId) .map((p) => p.position); return blockedWithPositions(pos, otherPositions, game.worldMap); } export function blockedWithPositions(position: Point, otherPositions: Point[], map: WorldMap) { if (isNaN(position.x) || isNaN(position.y)) { throw new Error(`NaN position in ${JSON.stringify(position)}`); } if (position.x < 0 || position.y < 0 || position.x >= map.width || position.y >= map.height) { return 'out of bounds'; } for (const layer of map.objectTiles) { if (layer[Math.floor(position.x)][Math.floor(position.y)] !== -1) { return 'world blocked'; } } for (const otherPosition of otherPositions) { if (distance(otherPosition, position) < COLLISION_THRESHOLD) { return 'player'; } } return null; } ================================================ FILE: convex/aiTown/player.ts ================================================ import { Infer, ObjectType, v } from 'convex/values'; import { Point, Vector, path, point, vector } from '../util/types'; import { GameId, parseGameId } from './ids'; import { playerId } from './ids'; import { PATHFINDING_TIMEOUT, PATHFINDING_BACKOFF, HUMAN_IDLE_TOO_LONG, MAX_HUMAN_PLAYERS, MAX_PATHFINDS_PER_STEP, } from '../constants'; import { pointsEqual, pathPosition } from '../util/geometry'; import { Game } from './game'; import { stopPlayer, findRoute, blocked, movePlayer } from './movement'; import { inputHandler } from './inputHandler'; import { characters } from '../../data/characters'; import { PlayerDescription } from './playerDescription'; const pathfinding = v.object({ destination: point, started: v.number(), state: v.union( v.object({ kind: v.literal('needsPath'), }), v.object({ kind: v.literal('waiting'), until: v.number(), }), v.object({ kind: v.literal('moving'), path, }), ), }); export type Pathfinding = Infer; export const activity = v.object({ description: v.string(), emoji: v.optional(v.string()), until: v.number(), }); export type Activity = Infer; export const serializedPlayer = { id: playerId, human: v.optional(v.string()), pathfinding: v.optional(pathfinding), activity: v.optional(activity), // The last time they did something. lastInput: v.number(), position: point, facing: vector, speed: v.number(), }; export type SerializedPlayer = ObjectType; export class Player { id: GameId<'players'>; human?: string; pathfinding?: Pathfinding; activity?: Activity; lastInput: number; position: Point; facing: Vector; speed: number; constructor(serialized: SerializedPlayer) { const { id, human, pathfinding, activity, lastInput, position, facing, speed } = serialized; this.id = parseGameId('players', id); this.human = human; this.pathfinding = pathfinding; this.activity = activity; this.lastInput = lastInput; this.position = position; this.facing = facing; this.speed = speed; } tick(game: Game, now: number) { if (this.human && this.lastInput < now - HUMAN_IDLE_TOO_LONG) { this.leave(game, now); } } tickPathfinding(game: Game, now: number) { // There's nothing to do if we're not moving. const { pathfinding, position } = this; if (!pathfinding) { return; } // Stop pathfinding if we've reached our destination. if (pathfinding.state.kind === 'moving' && pointsEqual(pathfinding.destination, position)) { stopPlayer(this); } // Stop pathfinding if we've timed out. if (pathfinding.started + PATHFINDING_TIMEOUT < now) { console.warn(`Timing out pathfinding for ${this.id}`); stopPlayer(this); } // Transition from "waiting" to "needsPath" if we're past the deadline. if (pathfinding.state.kind === 'waiting' && pathfinding.state.until < now) { pathfinding.state = { kind: 'needsPath' }; } // Perform pathfinding if needed. if (pathfinding.state.kind === 'needsPath' && game.numPathfinds < MAX_PATHFINDS_PER_STEP) { game.numPathfinds++; if (game.numPathfinds === MAX_PATHFINDS_PER_STEP) { console.warn(`Reached max pathfinds for this step`); } const route = findRoute(game, now, this, pathfinding.destination); if (route === null) { console.log(`Failed to route to ${JSON.stringify(pathfinding.destination)}`); stopPlayer(this); } else { if (route.newDestination) { console.warn( `Updating destination from ${JSON.stringify( pathfinding.destination, )} to ${JSON.stringify(route.newDestination)}`, ); pathfinding.destination = route.newDestination; } pathfinding.state = { kind: 'moving', path: route.path }; } } } tickPosition(game: Game, now: number) { // There's nothing to do if we're not moving. if (!this.pathfinding || this.pathfinding.state.kind !== 'moving') { this.speed = 0; return; } // Compute a candidate new position and check if it collides // with anything. const candidate = pathPosition(this.pathfinding.state.path as any, now); if (!candidate) { console.warn(`Path out of range of ${now} for ${this.id}`); return; } const { position, facing, velocity } = candidate; const collisionReason = blocked(game, now, position, this.id); if (collisionReason !== null) { const backoff = Math.random() * PATHFINDING_BACKOFF; console.warn(`Stopping path for ${this.id}, waiting for ${backoff}ms: ${collisionReason}`); this.pathfinding.state = { kind: 'waiting', until: now + backoff, }; return; } // Update the player's location. this.position = position; this.facing = facing; this.speed = velocity; } static join( game: Game, now: number, name: string, character: string, description: string, tokenIdentifier?: string, ) { if (tokenIdentifier) { let numHumans = 0; for (const player of game.world.players.values()) { if (player.human) { numHumans++; } if (player.human === tokenIdentifier) { throw new Error(`You are already in this game!`); } } if (numHumans >= MAX_HUMAN_PLAYERS) { throw new Error(`Only ${MAX_HUMAN_PLAYERS} human players allowed at once.`); } } let position; for (let attempt = 0; attempt < 10; attempt++) { const candidate = { x: Math.floor(Math.random() * game.worldMap.width), y: Math.floor(Math.random() * game.worldMap.height), }; if (blocked(game, now, candidate)) { continue; } position = candidate; break; } if (!position) { throw new Error(`Failed to find a free position!`); } const facingOptions = [ { dx: 1, dy: 0 }, { dx: -1, dy: 0 }, { dx: 0, dy: 1 }, { dx: 0, dy: -1 }, ]; const facing = facingOptions[Math.floor(Math.random() * facingOptions.length)]; if (!characters.find((c) => c.name === character)) { throw new Error(`Invalid character: ${character}`); } const playerId = game.allocId('players'); game.world.players.set( playerId, new Player({ id: playerId, human: tokenIdentifier, lastInput: now, position, facing, speed: 0, }), ); game.playerDescriptions.set( playerId, new PlayerDescription({ playerId, character, description, name, }), ); game.descriptionsModified = true; return playerId; } leave(game: Game, now: number) { // Stop our conversation if we're leaving the game. const conversation = [...game.world.conversations.values()].find((c) => c.participants.has(this.id), ); if (conversation) { conversation.stop(game, now); } game.world.players.delete(this.id); } serialize(): SerializedPlayer { const { id, human, pathfinding, activity, lastInput, position, facing, speed } = this; return { id, human, pathfinding, activity, lastInput, position, facing, speed, }; } } export const playerInputs = { join: inputHandler({ args: { name: v.string(), character: v.string(), description: v.string(), tokenIdentifier: v.optional(v.string()), }, handler: (game, now, args) => { Player.join(game, now, args.name, args.character, args.description, args.tokenIdentifier); return null; }, }), leave: inputHandler({ args: { playerId }, handler: (game, now, args) => { const playerId = parseGameId('players', args.playerId); const player = game.world.players.get(playerId); if (!player) { throw new Error(`Invalid player ID ${playerId}`); } player.leave(game, now); return null; }, }), moveTo: inputHandler({ args: { playerId, destination: v.union(point, v.null()), }, handler: (game, now, args) => { const playerId = parseGameId('players', args.playerId); const player = game.world.players.get(playerId); if (!player) { throw new Error(`Invalid player ID ${playerId}`); } if (args.destination) { movePlayer(game, now, player, args.destination); } else { stopPlayer(player); } return null; }, }), }; ================================================ FILE: convex/aiTown/playerDescription.ts ================================================ import { ObjectType, v } from 'convex/values'; import { GameId, parseGameId, playerId } from './ids'; export const serializedPlayerDescription = { playerId, name: v.string(), description: v.string(), character: v.string(), }; export type SerializedPlayerDescription = ObjectType; export class PlayerDescription { playerId: GameId<'players'>; name: string; description: string; character: string; constructor(serialized: SerializedPlayerDescription) { const { playerId, name, description, character } = serialized; this.playerId = parseGameId('players', playerId); this.name = name; this.description = description; this.character = character; } serialize(): SerializedPlayerDescription { const { playerId, name, description, character } = this; return { playerId, name, description, character, }; } } ================================================ FILE: convex/aiTown/schema.ts ================================================ import { v } from 'convex/values'; import { defineTable } from 'convex/server'; import { serializedPlayer } from './player'; import { serializedPlayerDescription } from './playerDescription'; import { serializedAgent } from './agent'; import { serializedAgentDescription } from './agentDescription'; import { serializedWorld } from './world'; import { serializedWorldMap } from './worldMap'; import { serializedConversation } from './conversation'; import { conversationId, playerId } from './ids'; export const aiTownTables = { // This table has a single document that stores all players, conversations, and agents. This // data is small and changes regularly over time. worlds: defineTable({ ...serializedWorld }), // Worlds can be started or stopped by the developer or paused for inactivity, and this // infrequently changing document tracks this world state. worldStatus: defineTable({ worldId: v.id('worlds'), isDefault: v.boolean(), engineId: v.id('engines'), lastViewed: v.number(), status: v.union(v.literal('running'), v.literal('stoppedByDeveloper'), v.literal('inactive')), }).index('worldId', ['worldId']), // This table contains the map data for a given world. Since it's a bit larger than the player // state and infrequently changes, we store it in a separate table. maps: defineTable({ worldId: v.id('worlds'), ...serializedWorldMap, }).index('worldId', ['worldId']), // Human readable text describing players and agents that's stored in separate tables, just like `maps`. playerDescriptions: defineTable({ worldId: v.id('worlds'), ...serializedPlayerDescription, }).index('worldId', ['worldId', 'playerId']), agentDescriptions: defineTable({ worldId: v.id('worlds'), ...serializedAgentDescription, }).index('worldId', ['worldId', 'agentId']), //The game engine doesn't want to track players that have left or conversations that are over, since // it wants to keep its managed state small. However, we may want to look at old conversations in the // UI or from the agent code. So, whenever we delete an entry from within the world's document, we // "archive" it within these tables. archivedPlayers: defineTable({ worldId: v.id('worlds'), ...serializedPlayer }).index('worldId', [ 'worldId', 'id', ]), archivedConversations: defineTable({ worldId: v.id('worlds'), id: conversationId, creator: playerId, created: v.number(), ended: v.number(), lastMessage: serializedConversation.lastMessage, numMessages: serializedConversation.numMessages, participants: v.array(playerId), }).index('worldId', ['worldId', 'id']), archivedAgents: defineTable({ worldId: v.id('worlds'), ...serializedAgent }).index('worldId', [ 'worldId', 'id', ]), // The agent layer wants to know what the last (completed) conversation was between two players, // so this table represents a labelled graph indicating which players have talked to each other. participatedTogether: defineTable({ worldId: v.id('worlds'), conversationId, player1: playerId, player2: playerId, ended: v.number(), }) .index('edge', ['worldId', 'player1', 'player2', 'ended']) .index('conversation', ['worldId', 'player1', 'conversationId']) .index('playerHistory', ['worldId', 'player1', 'ended']), }; ================================================ FILE: convex/aiTown/world.ts ================================================ import { ObjectType, v } from 'convex/values'; import { Conversation, serializedConversation } from './conversation'; import { Player, serializedPlayer } from './player'; import { Agent, serializedAgent } from './agent'; import { GameId, parseGameId, playerId } from './ids'; import { parseMap } from '../util/object'; export const historicalLocations = v.array( v.object({ playerId, location: v.bytes(), }), ); export const serializedWorld = { nextId: v.number(), conversations: v.array(v.object(serializedConversation)), players: v.array(v.object(serializedPlayer)), agents: v.array(v.object(serializedAgent)), historicalLocations: v.optional(historicalLocations), }; export type SerializedWorld = ObjectType; export class World { nextId: number; conversations: Map, Conversation>; players: Map, Player>; agents: Map, Agent>; historicalLocations?: Map, ArrayBuffer>; constructor(serialized: SerializedWorld) { const { nextId, historicalLocations } = serialized; this.nextId = nextId; this.conversations = parseMap(serialized.conversations, Conversation, (c) => c.id); this.players = parseMap(serialized.players, Player, (p) => p.id); this.agents = parseMap(serialized.agents, Agent, (a) => a.id); if (historicalLocations) { this.historicalLocations = new Map(); for (const { playerId, location } of historicalLocations) { this.historicalLocations.set(parseGameId('players', playerId), location); } } } playerConversation(player: Player): Conversation | undefined { return [...this.conversations.values()].find((c) => c.participants.has(player.id)); } serialize(): SerializedWorld { return { nextId: this.nextId, conversations: [...this.conversations.values()].map((c) => c.serialize()), players: [...this.players.values()].map((p) => p.serialize()), agents: [...this.agents.values()].map((a) => a.serialize()), historicalLocations: this.historicalLocations && [...this.historicalLocations.entries()].map(([playerId, location]) => ({ playerId, location, })), }; } } ================================================ FILE: convex/aiTown/worldMap.ts ================================================ import { Infer, ObjectType, v } from 'convex/values'; // `layer[position.x][position.y]` is the tileIndex or -1 if empty. const tileLayer = v.array(v.array(v.number())); export type TileLayer = Infer; const animatedSprite = { x: v.number(), y: v.number(), w: v.number(), h: v.number(), layer: v.number(), sheet: v.string(), animation: v.string(), }; export type AnimatedSprite = ObjectType; export const serializedWorldMap = { width: v.number(), height: v.number(), tileSetUrl: v.string(), // Width & height of tileset image, px. tileSetDimX: v.number(), tileSetDimY: v.number(), // Tile size in pixels (assume square) tileDim: v.number(), bgTiles: v.array(v.array(v.array(v.number()))), objectTiles: v.array(tileLayer), animatedSprites: v.array(v.object(animatedSprite)), }; export type SerializedWorldMap = ObjectType; export class WorldMap { width: number; height: number; tileSetUrl: string; tileSetDimX: number; tileSetDimY: number; tileDim: number; bgTiles: TileLayer[]; objectTiles: TileLayer[]; animatedSprites: AnimatedSprite[]; constructor(serialized: SerializedWorldMap) { this.width = serialized.width; this.height = serialized.height; this.tileSetUrl = serialized.tileSetUrl; this.tileSetDimX = serialized.tileSetDimX; this.tileSetDimY = serialized.tileSetDimY; this.tileDim = serialized.tileDim; this.bgTiles = serialized.bgTiles; this.objectTiles = serialized.objectTiles; this.animatedSprites = serialized.animatedSprites; } serialize(): SerializedWorldMap { return { width: this.width, height: this.height, tileSetUrl: this.tileSetUrl, tileSetDimX: this.tileSetDimX, tileSetDimY: this.tileSetDimY, tileDim: this.tileDim, bgTiles: this.bgTiles, objectTiles: this.objectTiles, animatedSprites: this.animatedSprites, }; } } ================================================ FILE: convex/constants.ts ================================================ export const ACTION_TIMEOUT = 120_000; // more time for local dev // export const ACTION_TIMEOUT = 60_000;// normally fine export const IDLE_WORLD_TIMEOUT = 5 * 60 * 1000; export const WORLD_HEARTBEAT_INTERVAL = 60 * 1000; export const MAX_STEP = 10 * 60 * 1000; export const TICK = 16; export const STEP_INTERVAL = 1000; export const PATHFINDING_TIMEOUT = 60 * 1000; export const PATHFINDING_BACKOFF = 1000; export const CONVERSATION_DISTANCE = 1.3; export const MIDPOINT_THRESHOLD = 4; export const TYPING_TIMEOUT = 15 * 1000; export const COLLISION_THRESHOLD = 0.75; // How many human players can be in a world at once. export const MAX_HUMAN_PLAYERS = 8; // Don't talk to anyone for 15s after having a conversation. export const CONVERSATION_COOLDOWN = 15000; // Don't do another activity for 10s after doing one. export const ACTIVITY_COOLDOWN = 10_000; // Don't talk to a player within 60s of talking to them. export const PLAYER_CONVERSATION_COOLDOWN = 60000; // Invite 80% of invites that come from other agents. export const INVITE_ACCEPT_PROBABILITY = 0.8; // Wait for 1m for invites to be accepted. export const INVITE_TIMEOUT = 60000; // Wait for another player to say something before jumping in. export const AWKWARD_CONVERSATION_TIMEOUT = 60_000; // more time locally // export const AWKWARD_CONVERSATION_TIMEOUT = 20_000; // Leave a conversation after participating too long. export const MAX_CONVERSATION_DURATION = 10 * 60_000; // more time locally // export const MAX_CONVERSATION_DURATION = 2 * 60_000; // Leave a conversation if it has more than 8 messages; export const MAX_CONVERSATION_MESSAGES = 8; // Wait for 1s after sending an input to the engine. We can remove this // once we can await on an input being processed. export const INPUT_DELAY = 1000; // How many memories to get from the agent's memory. // This is over-fetched by 10x so we can prioritize memories by more than relevance. export const NUM_MEMORIES_TO_SEARCH = 3; // Wait for at least two seconds before sending another message. export const MESSAGE_COOLDOWN = 2000; // Don't run a turn of the agent more than once a second. export const AGENT_WAKEUP_THRESHOLD = 1000; // How old we let memories be before we vacuum them export const VACUUM_MAX_AGE = 2 * 7 * 24 * 60 * 60 * 1000; export const DELETE_BATCH_SIZE = 64; export const HUMAN_IDLE_TOO_LONG = 5 * 60 * 1000; export const ACTIVITIES = [ { description: 'reading a book', emoji: '📖', duration: 60_000 }, { description: 'daydreaming', emoji: '🤔', duration: 60_000 }, { description: 'gardening', emoji: '🥕', duration: 60_000 }, ]; export const ENGINE_ACTION_DURATION = 30000; // Bound the number of pathfinding searches we do per game step. export const MAX_PATHFINDS_PER_STEP = 16; export const DEFAULT_NAME = 'Me'; ================================================ FILE: convex/crons.ts ================================================ import { cronJobs } from 'convex/server'; import { DELETE_BATCH_SIZE, IDLE_WORLD_TIMEOUT, VACUUM_MAX_AGE } from './constants'; import { internal } from './_generated/api'; import { internalMutation } from './_generated/server'; import { TableNames } from './_generated/dataModel'; import { v } from 'convex/values'; const crons = cronJobs(); crons.interval( 'stop inactive worlds', { seconds: IDLE_WORLD_TIMEOUT / 1000 }, internal.world.stopInactiveWorlds, ); crons.interval('restart dead worlds', { seconds: 60 }, internal.world.restartDeadWorlds); crons.daily('vacuum old entries', { hourUTC: 4, minuteUTC: 20 }, internal.crons.vacuumOldEntries); export default crons; const TablesToVacuum: TableNames[] = [ // Un-comment this to also clean out old conversations. // 'conversationMembers', 'conversations', 'messages', // Inputs aren't useful unless you're trying to replay history. // If you want to support that, you should add a snapshot table, so you can // replay from a certain time period. Or stop vacuuming inputs and replay from // the beginning of time 'inputs', // We can keep memories without their embeddings for inspection, but we won't // retrieve them when searching memories via vector search. 'memories', // We can vacuum fewer tables without serious consequences, but the only // one that will cause issues over time is having >>100k vectors. 'memoryEmbeddings', ]; export const vacuumOldEntries = internalMutation({ args: {}, handler: async (ctx, args) => { const before = Date.now() - VACUUM_MAX_AGE; for (const tableName of TablesToVacuum) { console.log(`Checking ${tableName}...`); const exists = await ctx.db .query(tableName) .withIndex('by_creation_time', (q) => q.lt('_creationTime', before)) .first(); if (exists) { console.log(`Vacuuming ${tableName}...`); await ctx.scheduler.runAfter(0, internal.crons.vacuumTable, { tableName, before, cursor: null, soFar: 0, }); } } }, }); export const vacuumTable = internalMutation({ args: { tableName: v.string(), before: v.number(), cursor: v.union(v.string(), v.null()), soFar: v.number(), }, handler: async (ctx, { tableName, before, cursor, soFar }) => { const results = await ctx.db .query(tableName as TableNames) .withIndex('by_creation_time', (q) => q.lt('_creationTime', before)) .paginate({ cursor, numItems: DELETE_BATCH_SIZE }); for (const row of results.page) { await ctx.db.delete(row._id); } if (!results.isDone) { await ctx.scheduler.runAfter(0, internal.crons.vacuumTable, { tableName, before, soFar: results.page.length + soFar, cursor: results.continueCursor, }); } else { console.log(`Vacuumed ${soFar + results.page.length} entries from ${tableName}`); } }, }); ================================================ FILE: convex/engine/abstractGame.ts ================================================ import { ConvexError, Infer, Value, v } from 'convex/values'; import { Doc, Id } from '../_generated/dataModel'; import { ActionCtx, DatabaseReader, MutationCtx, internalQuery } from '../_generated/server'; import { engine } from '../engine/schema'; import { internal } from '../_generated/api'; export abstract class AbstractGame { abstract tickDuration: number; abstract stepDuration: number; abstract maxTicksPerStep: number; abstract maxInputsPerStep: number; constructor(public engine: Doc<'engines'>) {} abstract handleInput(now: number, name: string, args: object): Value; abstract tick(now: number): void; // Optional callback at the beginning of each step. beginStep(now: number) {} abstract saveStep(ctx: ActionCtx, engineUpdate: EngineUpdate): Promise; async runStep(ctx: ActionCtx, now: number) { const inputs = await ctx.runQuery(internal.engine.abstractGame.loadInputs, { engineId: this.engine._id, processedInputNumber: this.engine.processedInputNumber, max: this.maxInputsPerStep, }); const lastStepTs = this.engine.currentTime; const startTs = lastStepTs ? lastStepTs + this.tickDuration : now; let currentTs = startTs; let inputIndex = 0; let numTicks = 0; let processedInputNumber = this.engine.processedInputNumber; const completedInputs = []; this.beginStep(currentTs); while (numTicks < this.maxTicksPerStep) { numTicks += 1; // Collect all of the inputs for this tick. const tickInputs = []; while (inputIndex < inputs.length) { const input = inputs[inputIndex]; if (input.received > currentTs) { break; } inputIndex += 1; processedInputNumber = input.number; tickInputs.push(input); } // Feed the inputs to the game. for (const input of tickInputs) { let returnValue; try { const value = this.handleInput(currentTs, input.name, input.args); returnValue = { kind: 'ok' as const, value }; } catch (e: any) { console.error(`Input ${input._id} failed: ${e.message}`); returnValue = { kind: 'error' as const, message: e.message }; } completedInputs.push({ inputId: input._id, returnValue }); } // Simulate the game forward one tick. this.tick(currentTs); const candidateTs = currentTs + this.tickDuration; if (now < candidateTs) { break; } currentTs = candidateTs; } // Commit the step by moving time forward, consuming our inputs, and saving the game's state. const expectedGenerationNumber = this.engine.generationNumber; this.engine.currentTime = currentTs; this.engine.lastStepTs = lastStepTs; this.engine.generationNumber += 1; this.engine.processedInputNumber = processedInputNumber; const { _id, _creationTime, ...engine } = this.engine; const engineUpdate = { engine, completedInputs, expectedGenerationNumber }; await this.saveStep(ctx, engineUpdate); console.debug(`Simulated from ${startTs} to ${currentTs} (${currentTs - startTs}ms)`); } } const completedInput = v.object({ inputId: v.id('inputs'), returnValue: v.union( v.object({ kind: v.literal('ok'), value: v.any(), }), v.object({ kind: v.literal('error'), message: v.string(), }), ), }); export const engineUpdate = v.object({ engine, expectedGenerationNumber: v.number(), completedInputs: v.array(completedInput), }); export type EngineUpdate = Infer; export async function loadEngine( db: DatabaseReader, engineId: Id<'engines'>, generationNumber: number, ) { const engine = await db.get(engineId); if (!engine) { throw new Error(`No engine found with id ${engineId}`); } if (!engine.running) { throw new ConvexError({ kind: 'engineNotRunning', message: `Engine ${engineId} is not running`, }); } if (engine.generationNumber !== generationNumber) { throw new ConvexError({ kind: 'generationNumber', message: 'Generation number mismatch' }); } return engine; } export async function engineInsertInput( ctx: MutationCtx, engineId: Id<'engines'>, name: string, args: any, ): Promise> { const now = Date.now(); const prevInput = await ctx.db .query('inputs') .withIndex('byInputNumber', (q) => q.eq('engineId', engineId)) .order('desc') .first(); const number = prevInput ? prevInput.number + 1 : 0; const inputId = await ctx.db.insert('inputs', { engineId, number, name, args, received: now, }); return inputId; } export const loadInputs = internalQuery({ args: { engineId: v.id('engines'), processedInputNumber: v.optional(v.number()), max: v.number(), }, handler: async (ctx, args) => { return await ctx.db .query('inputs') .withIndex('byInputNumber', (q) => q.eq('engineId', args.engineId).gt('number', args.processedInputNumber ?? -1), ) .order('asc') .take(args.max); }, }); export async function applyEngineUpdate( ctx: MutationCtx, engineId: Id<'engines'>, update: EngineUpdate, ) { const engine = await loadEngine(ctx.db, engineId, update.expectedGenerationNumber); if ( engine.currentTime && update.engine.currentTime && update.engine.currentTime < engine.currentTime ) { throw new Error('Time moving backwards'); } await ctx.db.replace(engine._id, update.engine); for (const completedInput of update.completedInputs) { const input = await ctx.db.get(completedInput.inputId); if (!input) { throw new Error(`Input ${completedInput.inputId} not found`); } if (input.returnValue) { throw new Error(`Input ${completedInput.inputId} already completed`); } input.returnValue = completedInput.returnValue; await ctx.db.replace(input._id, input); } } ================================================ FILE: convex/engine/historicalObject.test.ts ================================================ import { History, packSampleRecord, unpackSampleRecord } from './historicalObject'; describe('HistoricalObject', () => { test('pack sample record roundtrips', () => { let data: Record = { x: { initialValue: 0, samples: [ { time: 1696021246740, value: 1 }, { time: 1696021246756, value: 2 }, { time: 1696021246772, value: 3 }, { time: 1696021246788, value: 4 }, ], }, y: { initialValue: 140.2, samples: [ { time: 1696021246740, value: 169.7 }, { time: 1696021246756, value: 237.59 }, { time: 1696021246772, value: 344.44 }, { time: 1696021246788, value: 489.13 }, ], }, }; const fields = [ { name: 'x', precision: 4 }, { name: 'y', precision: 4 }, ]; const packed = packSampleRecord(fields, data); const unpacked = unpackSampleRecord(fields, packed); const maxError = Math.max(1 / (1 << 4), 1e-8); expect(Object.keys(data)).toEqual(Object.keys(unpacked)); for (const key of Object.keys(data)) { const { initialValue, samples } = data[key]; const { initialValue: unpackedInitialValue, samples: unpackedSamples } = unpacked[key]; expect(Math.abs(initialValue - unpackedInitialValue)).toBeLessThanOrEqual(maxError); expect(samples.length).toEqual(unpackedSamples.length); for (let i = 0; i < samples.length; i++) { const sample = samples[i]; const unpackedSample = unpackedSamples[i]; expect(sample.time).toEqual(unpackedSample.time); expect(Math.abs(sample.value - unpackedSample.value)).toBeLessThanOrEqual(maxError); } } }); }); ================================================ FILE: convex/engine/historicalObject.ts ================================================ import { xxHash32 } from '../util/xxhash'; import { compressSigned, uncompressSigned } from '../util/FastIntegerCompression'; import { runLengthEncode, deltaEncode, quantize, deltaDecode, runLengthDecode, unquantize, } from '../util/compression'; // `HistoricalObject`s require the developer to pass in the // field names that'll be tracked and sent down to the client. // // By default, the historical tracking will round each floating point // value to an integer. The developer can specify more or less precision // via the `precision` parameter: the table's quantization will maintain // less than `1 / 2^precision` error. Note that higher precision values // imply less error. export type FieldConfig = Array; // `HistoricalObject`s support at most 16 fields. const MAX_FIELDS = 16; const PACKED_VERSION = 1; type NormalizedFieldConfig = Array<{ name: string; precision: number; }>; // The `History` structure represents the history of a continuous // value over all bounded time. Each sample represents a line // segment that's extends to the previous sample's time inclusively // and to the sample's time non-inclusively. We track an `initialValue` // that goes to `-\infty` up until the first sample, and the final // sample persists out to `+\infty`. // ``` // ^ // position // | // samples[0].value - | x---------------o // | // samples[1].value - | x--------> // | // initialValue - <---------o // | // ------------------------------> time // | | // samples[0].time samples[1].time // ``` export type History = { initialValue: number; samples: Sample[]; }; export type Sample = { time: number; value: number; }; // `HistoricalObject` tracks a set of numeric fields over time and // supports compressing the fields' histories into a binary buffer. // This can be useful for continuous properties like position, where // we'd want to smoothly replay their tick-by-tick progress at a high // frame rate on the client. // // `HistoricalObject`s have a few limitations: // - Documents in a historical can only have up to 16 fields. // - The historical tracking only applies to a specified list of fields, // and these fields must match between the client and server. export class HistoricalObject> { startTs?: number; fieldConfig: NormalizedFieldConfig; data: T; history: Record = {}; constructor(fields: FieldConfig, initialValue: T) { if (fields.length >= MAX_FIELDS) { throw new Error(`HistoricalObject can have at most ${MAX_FIELDS} fields.`); } this.fieldConfig = normalizeFieldConfig(fields); this.checkShape(initialValue); this.data = initialValue; } historyLength() { return Object.values(this.history) .map((h) => h.samples.length) .reduce((a, b) => a + b, 0); } checkShape(data: any) { for (const [key, value] of Object.entries(data)) { if (!this.fieldConfig.find((f) => f.name === key)) { throw new Error(`Cannot set undeclared field '${key}'`); } if (typeof value !== 'number') { throw new Error( `HistoricalObject only supports numeric values, found: ${JSON.stringify(value)}`, ); } } } update(now: number, data: T) { this.checkShape(data); for (const [key, value] of Object.entries(data)) { const currentValue = this.data[key]; if (currentValue !== value) { let history = this.history[key]; if (!history) { this.history[key] = history = { initialValue: currentValue, samples: [] }; } const { samples } = history; let inserted = false; if (samples.length > 0) { const last = samples[samples.length - 1]; if (now < last.time) { throw new Error(`Server time moving backwards: ${now} < ${last.time}`); } if (now === last.time) { last.value = value; inserted = true; } } if (!inserted) { samples.push({ time: now, value }); } } } this.data = data; } pack(): ArrayBuffer | null { if (this.historyLength() === 0) { return null; } return packSampleRecord(this.fieldConfig, this.history); } } // Pack (normalized) field configuration into a binary buffer. // // Format: // ``` // [ u8 version ] // for each field config: // [ u8 field name length ] // [ UTF8 encoded field name ] // [ u8 precision ] // ``` function packFieldConfig(fields: NormalizedFieldConfig) { const out = new ArrayBuffer(1024); const outView = new DataView(out); let pos = 0; outView.setUint8(pos, PACKED_VERSION); pos += 1; const encoder = new TextEncoder(); for (const fieldConfig of fields) { const name = encoder.encode(fieldConfig.name); outView.setUint8(pos, name.length); pos += 1; new Uint8Array(out, pos, name.length).set(name); pos += name.length; outView.setUint8(pos, fieldConfig.precision); pos += 1; } return out.slice(0, pos); } // Pack a document's sample record into a binary buffer. // // We encode each field's history with a few layered forms of // compression: // 1. Quantization: Turn each floating point number into an integer // by multiplying by 2^precision and then `Math.floor()`. // 2. Delta encoding: Assume that values are continuous and don't // abruptly change over time, so their differences will be small. // This step turns the large integers from (1) into small ones. // 3. Run length encoding (optional): Assume that some quantities // in the system will have constant velocity, so encode `k` // repetitions of `n` as `[k, n]`. If run length encoding doesn't // make (2) smaller, we skip it. // 4. Varint encoding: Using FastIntegerCompression.js, we use a // variable length integer encoding that uses fewer bytes for // smaller numbers. // // Format: // ``` // [ 4 byte xxhash of packed field config ] // // for each set field: // [ 0 0 0 useRLE? ] // [ u4 field number ] // // Sample timestamps: // [ u64le initial timestamp ] // [ u16le timestamp buffer length ] // [ vint(RLE(delta(remaining timestamps)))] // // Sample values: // [ u16le value buffer length ] // [ vint(RLE?(delta([initialValue, ...values])))] // ``` export function packSampleRecord( fields: NormalizedFieldConfig, sampleRecord: Record, ): ArrayBuffer { const out = new ArrayBuffer(65536); const outView = new DataView(out); let pos = 0; const configHash = xxHash32(new Uint8Array(packFieldConfig(fields))); outView.setUint32(pos, configHash, true); pos += 4; for (let fieldNumber = 0; fieldNumber < fields.length; fieldNumber += 1) { const { name, precision } = fields[fieldNumber]; const history = sampleRecord[name]; if (!history || history.samples.length === 0) { continue; } const timestamps = history.samples.map((s) => Math.floor(s.time)); const initialTimestamp = timestamps[0]; const encodedTimestamps = runLengthEncode(deltaEncode(timestamps.slice(1), initialTimestamp)); const compressedTimestamps = compressSigned(encodedTimestamps); if (compressedTimestamps.byteLength >= 1 << 16) { throw new Error(`Compressed buffer too long: ${compressedTimestamps.byteLength}`); } const values = [history.initialValue, ...history.samples.map((s) => s.value)]; const quantized = quantize(values, precision); const deltaEncoded = deltaEncode(quantized); const runLengthEncoded = runLengthEncode(deltaEncoded); // Decide if we're going to run length encode the values based on whether // it actually made the encoded buffer smaller. const useRLE = runLengthEncoded.length < deltaEncoded.length; let fieldHeader = fieldNumber; if (useRLE) { fieldHeader |= 1 << 4; } const encoded = useRLE ? runLengthEncoded : deltaEncoded; const compressed = compressSigned(encoded); if (compressed.byteLength >= 1 << 16) { throw new Error(`Compressed buffer too long: ${compressed.byteLength}`); } outView.setUint8(pos, fieldHeader); pos += 1; outView.setBigUint64(pos, BigInt(initialTimestamp), true); pos += 8; outView.setUint16(pos, compressedTimestamps.byteLength, true); pos += 2; new Uint8Array(out, pos, compressedTimestamps.byteLength).set( new Uint8Array(compressedTimestamps), ); pos += compressedTimestamps.byteLength; outView.setUint16(pos, compressed.byteLength, true); pos += 2; new Uint8Array(out, pos, compressed.byteLength).set(new Uint8Array(compressed)); pos += compressed.byteLength; } return out.slice(0, pos); } export function unpackSampleRecord(fields: FieldConfig, buffer: ArrayBuffer) { const view = new DataView(buffer); let pos = 0; const normalizedFields = normalizeFieldConfig(fields); const expectedConfigHash = xxHash32(new Uint8Array(packFieldConfig(normalizedFields))); const configHash = view.getUint32(pos, true); pos += 4; if (configHash !== expectedConfigHash) { throw new Error(`Config hash mismatch: ${configHash} !== ${expectedConfigHash}`); } const out = {} as Record; while (pos < buffer.byteLength) { const fieldHeader = view.getUint8(pos); pos += 1; const fieldNumber = fieldHeader & 0b00001111; const useRLE = (fieldHeader & (1 << 4)) !== 0; const fieldConfig = normalizedFields[fieldNumber]; if (!fieldConfig) { throw new Error(`Invalid field number: ${fieldNumber}`); } const initialTimestamp = Number(view.getBigUint64(pos, true)); pos += 8; const compressedTimestampLength = view.getUint16(pos, true); pos += 2; const compressedTimestampBuffer = buffer.slice(pos, pos + compressedTimestampLength); pos += compressedTimestampLength; const timestamps = [ initialTimestamp, ...deltaDecode( runLengthDecode(uncompressSigned(compressedTimestampBuffer)), initialTimestamp, ), ]; const compressedLength = view.getUint16(pos, true); pos += 2; const compressedBuffer = buffer.slice(pos, pos + compressedLength); pos += compressedLength; const encoded = uncompressSigned(compressedBuffer); const deltaEncoded = useRLE ? runLengthDecode(encoded) : encoded; const quantized = deltaDecode(deltaEncoded); const values = unquantize(quantized, fieldConfig.precision); if (timestamps.length + 1 !== values.length) { throw new Error(`Invalid sample record: ${timestamps.length} + 1 !== ${values.length}`); } const initialValue = values[0]; const samples = []; for (let i = 0; i < timestamps.length; i++) { const time = timestamps[i]; const value = values[i + 1]; samples.push({ value, time }); } const history = { initialValue, samples }; out[fieldConfig.name] = history; } return out; } function normalizeFieldConfig(fields: FieldConfig): NormalizedFieldConfig { return fields.map((f) => (typeof f === 'string' ? { name: f, precision: 0 } : f)); } ================================================ FILE: convex/engine/schema.ts ================================================ import { defineTable } from 'convex/server'; import { Infer, v } from 'convex/values'; const input = v.object({ // Inputs are scoped to a single engine. engineId: v.id('engines'), // Monotonically increasing input number within a world starting at 0. number: v.number(), // Name of the input handler to run. name: v.string(), // Dynamically typed arguments and return value for the input handler. We'll // provide type safety at a higher layer. args: v.any(), returnValue: v.optional( v.union( v.object({ kind: v.literal('ok'), value: v.any(), }), v.object({ kind: v.literal('error'), message: v.string(), }), ), ), // Timestamp when the server received the input. This timestamp is best-effort, // since we don't guarantee strict monotonicity here. So, an input may not get // assigned to the engine step whose time interval contains this timestamp. received: v.number(), }); export const engine = v.object({ // What is the current simulation time for the engine? Monotonically increasing. currentTime: v.optional(v.number()), // What was `currentTime` for the preceding step of the engine? lastStepTs: v.optional(v.number()), // How far has the engine processed in the input queue? processedInputNumber: v.optional(v.number()), running: v.boolean(), // Monotonically increasing counter that serializes all engine runs. If we ever // end up with two steps overlapping in time, this counter will force them to // conflict. generationNumber: v.number(), }); export type Engine = Infer; export const engineTables = { inputs: defineTable(input).index('byInputNumber', ['engineId', 'number']), engines: defineTable(engine), }; ================================================ FILE: convex/http.ts ================================================ import { httpRouter } from 'convex/server'; import { handleReplicateWebhook } from './music'; const http = httpRouter(); http.route({ path: '/replicate_webhook', method: 'POST', handler: handleReplicateWebhook, }); export default http; ================================================ FILE: convex/init.ts ================================================ import { v } from 'convex/values'; import { internal } from './_generated/api'; import { DatabaseReader, MutationCtx, mutation } from './_generated/server'; import { Descriptions } from '../data/characters'; import * as map from '../data/gentle'; import { insertInput } from './aiTown/insertInput'; import { Id } from './_generated/dataModel'; import { createEngine } from './aiTown/main'; import { ENGINE_ACTION_DURATION } from './constants'; import { detectMismatchedLLMProvider } from './util/llm'; const init = mutation({ args: { numAgents: v.optional(v.number()), }, handler: async (ctx, args) => { detectMismatchedLLMProvider(); const { worldStatus, engine } = await getOrCreateDefaultWorld(ctx); if (worldStatus.status !== 'running') { console.warn( `Engine ${engine._id} is not active! Run "npx convex run testing:resume" to restart it.`, ); return; } const shouldCreate = await shouldCreateAgents( ctx.db, worldStatus.worldId, worldStatus.engineId, ); if (shouldCreate) { const toCreate = args.numAgents !== undefined ? args.numAgents : Descriptions.length; for (let i = 0; i < toCreate; i++) { await insertInput(ctx, worldStatus.worldId, 'createAgent', { descriptionIndex: i % Descriptions.length, }); } } }, }); export default init; async function getOrCreateDefaultWorld(ctx: MutationCtx) { const now = Date.now(); let worldStatus = await ctx.db .query('worldStatus') .filter((q) => q.eq(q.field('isDefault'), true)) .unique(); if (worldStatus) { const engine = (await ctx.db.get(worldStatus.engineId))!; return { worldStatus, engine }; } const engineId = await createEngine(ctx); const engine = (await ctx.db.get(engineId))!; const worldId = await ctx.db.insert('worlds', { nextId: 0, agents: [], conversations: [], players: [], }); const worldStatusId = await ctx.db.insert('worldStatus', { engineId: engineId, isDefault: true, lastViewed: now, status: 'running', worldId: worldId, }); worldStatus = (await ctx.db.get(worldStatusId))!; await ctx.db.insert('maps', { worldId, width: map.mapwidth, height: map.mapheight, tileSetUrl: map.tilesetpath, tileSetDimX: map.tilesetpxw, tileSetDimY: map.tilesetpxh, tileDim: map.tiledim, bgTiles: map.bgtiles, objectTiles: map.objmap, animatedSprites: map.animatedsprites, }); await ctx.scheduler.runAfter(0, internal.aiTown.main.runStep, { worldId, generationNumber: engine.generationNumber, maxDuration: ENGINE_ACTION_DURATION, }); return { worldStatus, engine }; } async function shouldCreateAgents( db: DatabaseReader, worldId: Id<'worlds'>, engineId: Id<'engines'>, ) { const world = await db.get(worldId); if (!world) { throw new Error(`Invalid world ID: ${worldId}`); } if (world.agents.length > 0) { return false; } const unactionedJoinInputs = await db .query('inputs') .withIndex('byInputNumber', (q) => q.eq('engineId', engineId)) .order('asc') .filter((q) => q.eq(q.field('name'), 'createAgent')) .filter((q) => q.eq(q.field('returnValue'), undefined)) .first(); if (unactionedJoinInputs) { return false; } return true; } ================================================ FILE: convex/messages.ts ================================================ import { v } from 'convex/values'; import { mutation, query } from './_generated/server'; import { insertInput } from './aiTown/insertInput'; import { conversationId, playerId } from './aiTown/ids'; export const listMessages = query({ args: { worldId: v.id('worlds'), conversationId, }, handler: async (ctx, args) => { const messages = await ctx.db .query('messages') .withIndex('conversationId', (q) => q.eq('worldId', args.worldId).eq('conversationId', args.conversationId)) .collect(); const out = []; for (const message of messages) { const playerDescription = await ctx.db .query('playerDescriptions') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('playerId', message.author)) .first(); if (!playerDescription) { throw new Error(`Invalid author ID: ${message.author}`); } out.push({ ...message, authorName: playerDescription.name }); } return out; }, }); export const writeMessage = mutation({ args: { worldId: v.id('worlds'), conversationId, messageUuid: v.string(), playerId, text: v.string(), }, handler: async (ctx, args) => { await ctx.db.insert('messages', { conversationId: args.conversationId, author: args.playerId, messageUuid: args.messageUuid, text: args.text, worldId: args.worldId, }); await insertInput(ctx, args.worldId, 'finishSendingMessage', { conversationId: args.conversationId, playerId: args.playerId, timestamp: Date.now(), }); }, }); ================================================ FILE: convex/music.ts ================================================ import { v } from 'convex/values'; import { query, internalMutation } from './_generated/server'; import Replicate, { WebhookEventType } from 'replicate'; import { httpAction, internalAction } from './_generated/server'; import { internal, api } from './_generated/api'; function client(): Replicate { const replicate = new Replicate({ auth: process.env.REPLICATE_API_TOKEN || '', }); return replicate; } function replicateAvailable(): boolean { return !!process.env.REPLICATE_API_TOKEN; } export const insertMusic = internalMutation({ args: { storageId: v.string(), type: v.union(v.literal('background'), v.literal('player')) }, handler: async (ctx, args) => { await ctx.db.insert('music', { storageId: args.storageId, type: args.type, }); }, }); export const getBackgroundMusic = query({ handler: async (ctx) => { const music = await ctx.db .query('music') .filter((entry) => entry.eq(entry.field('type'), 'background')) .order('desc') .first(); if (!music) { return '/ai-town/assets/background.mp3'; } const url = await ctx.storage.getUrl(music.storageId); if (!url) { throw new Error(`Invalid storage ID: ${music.storageId}`); } return url; }, }); export const enqueueBackgroundMusicGeneration = internalAction({ handler: async (ctx): Promise => { if (!replicateAvailable()) { return; } const worldStatus = await ctx.runQuery(api.world.defaultWorldStatus); if (!worldStatus) { console.log('No active default world, returning.'); return; } // TODO: MusicGen-Large on Replicate only allows 30 seconds. Use MusicGen-Small for longer? await generateMusic('16-bit RPG adventure game with wholesome vibe', 30); }, }); export const handleReplicateWebhook = httpAction(async (ctx, request) => { const req = await request.json(); if (req.id) { const prediction = await client().predictions.get(req.id); const response = await fetch(prediction.output); const music = await response.blob(); const storageId = await ctx.storage.store(music); await ctx.runMutation(internal.music.insertMusic, { type: 'background', storageId }); } return new Response(); }); enum MusicGenNormStrategy { Clip = 'clip', Loudness = 'loudness', Peak = 'peak', Rms = 'rms', } enum MusicGenFormat { wav = 'wav', mp3 = 'mp3', } /** * * @param prompt A description of the music you want to generate. * @param duration Duration of the generated audio in seconds. * @param webhook webhook URL for Replicate to call when @param webhook_events_filter is triggered * @param webhook_events_filter Array of event names to filter the webhook. See https://replicate.com/docs/reference/http#predictions.create--webhook_events_filter * @param normalization_strategy Strategy for normalizing audio. * @param top_k Reduces sampling to the k most likely tokens. * @param top_p Reduces sampling to tokens with cumulative probability of p. When set to `0` (default), top_k sampling is used. * @param temperature Controls the 'conservativeness' of the sampling process. Higher temperature means more diversity. * @param classifer_free_gudance Increases the influence of inputs on the output. Higher values produce lower-varience outputs that adhere more closely to inputs. * @param output_format Output format for generated audio. See @ * @param seed Seed for random number generator. If None or -1, a random seed will be used. * @returns object containing metadata of the prediction with ID to fetch once result is completed */ export async function generateMusic( prompt: string, duration: number, webhook: string = process.env.CONVEX_SITE_URL + '/replicate_webhook' || '', webhook_events_filter: [WebhookEventType] = ['completed'], normalization_strategy: MusicGenNormStrategy = MusicGenNormStrategy.Peak, output_format: MusicGenFormat = MusicGenFormat.mp3, top_k = 250, top_p = 0, temperature = 1, classifer_free_gudance = 3, seed = -1, model_version = 'large', ) { if (!replicateAvailable()) { throw new Error('Replicate API token not set'); } return await client().predictions.create({ // https://replicate.com/facebookresearch/musicgen/versions/7a76a8258b23fae65c5a22debb8841d1d7e816b75c2f24218cd2bd8573787906 version: '7a76a8258b23fae65c5a22debb8841d1d7e816b75c2f24218cd2bd8573787906', input: { model_version, prompt, duration, normalization_strategy, top_k, top_p, temperature, classifer_free_gudance, output_format, seed, }, webhook, webhook_events_filter, }); } ================================================ FILE: convex/schema.ts ================================================ import { defineSchema, defineTable } from 'convex/server'; import { v } from 'convex/values'; import { agentTables } from './agent/schema'; import { aiTownTables } from './aiTown/schema'; import { conversationId, playerId } from './aiTown/ids'; import { engineTables } from './engine/schema'; export default defineSchema({ music: defineTable({ storageId: v.string(), type: v.union(v.literal('background'), v.literal('player')), }), messages: defineTable({ conversationId, messageUuid: v.string(), author: playerId, text: v.string(), worldId: v.optional(v.id('worlds')), }) .index('conversationId', ['worldId', 'conversationId']) .index('messageUuid', ['conversationId', 'messageUuid']), ...agentTables, ...aiTownTables, ...engineTables, }); ================================================ FILE: convex/testing.ts ================================================ import { Id, TableNames } from './_generated/dataModel'; import { internal } from './_generated/api'; import { DatabaseReader, internalAction, internalMutation, mutation, query, } from './_generated/server'; import { v } from 'convex/values'; import schema from './schema'; import { DELETE_BATCH_SIZE } from './constants'; import { kickEngine, startEngine, stopEngine } from './aiTown/main'; import { insertInput } from './aiTown/insertInput'; import { fetchEmbedding } from './util/llm'; import { chatCompletion } from './util/llm'; import { startConversationMessage } from './agent/conversation'; import { GameId } from './aiTown/ids'; // Clear all of the tables except for the embeddings cache. const excludedTables: Array = ['embeddingsCache']; export const wipeAllTables = internalMutation({ handler: async (ctx) => { for (const tableName of Object.keys(schema.tables)) { if (excludedTables.includes(tableName as TableNames)) { continue; } await ctx.scheduler.runAfter(0, internal.testing.deletePage, { tableName, cursor: null }); } }, }); export const deletePage = internalMutation({ args: { tableName: v.string(), cursor: v.union(v.string(), v.null()), }, handler: async (ctx, args) => { const results = await ctx.db .query(args.tableName as TableNames) .paginate({ cursor: args.cursor, numItems: DELETE_BATCH_SIZE }); for (const row of results.page) { await ctx.db.delete(row._id); } if (!results.isDone) { await ctx.scheduler.runAfter(0, internal.testing.deletePage, { tableName: args.tableName, cursor: results.continueCursor, }); } }, }); export const kick = internalMutation({ handler: async (ctx) => { const { worldStatus } = await getDefaultWorld(ctx.db); await kickEngine(ctx, worldStatus.worldId); }, }); export const stopAllowed = query({ handler: async () => { return !process.env.STOP_NOT_ALLOWED; }, }); export const stop = mutation({ handler: async (ctx) => { if (process.env.STOP_NOT_ALLOWED) throw new Error('Stop not allowed'); const { worldStatus, engine } = await getDefaultWorld(ctx.db); if (worldStatus.status === 'inactive' || worldStatus.status === 'stoppedByDeveloper') { if (engine.running) { throw new Error(`Engine ${engine._id} isn't stopped?`); } console.debug(`World ${worldStatus.worldId} is already inactive`); return; } console.log(`Stopping engine ${engine._id}...`); await ctx.db.patch(worldStatus._id, { status: 'stoppedByDeveloper' }); await stopEngine(ctx, worldStatus.worldId); }, }); export const resume = mutation({ handler: async (ctx) => { const { worldStatus, engine } = await getDefaultWorld(ctx.db); if (worldStatus.status === 'running') { if (!engine.running) { throw new Error(`Engine ${engine._id} isn't running?`); } console.debug(`World ${worldStatus.worldId} is already running`); return; } console.log( `Resuming engine ${engine._id} for world ${worldStatus.worldId} (state: ${worldStatus.status})...`, ); await ctx.db.patch(worldStatus._id, { status: 'running' }); await startEngine(ctx, worldStatus.worldId); }, }); export const archive = internalMutation({ handler: async (ctx) => { const { worldStatus, engine } = await getDefaultWorld(ctx.db); if (engine.running) { throw new Error(`Engine ${engine._id} is still running!`); } console.log(`Archiving world ${worldStatus.worldId}...`); await ctx.db.patch(worldStatus._id, { isDefault: false }); }, }); async function getDefaultWorld(db: DatabaseReader) { const worldStatus = await db .query('worldStatus') .filter((q) => q.eq(q.field('isDefault'), true)) .first(); if (!worldStatus) { throw new Error('No default world found'); } const engine = await db.get(worldStatus.engineId); if (!engine) { throw new Error(`Engine ${worldStatus.engineId} not found`); } return { worldStatus, engine }; } export const debugCreatePlayers = internalMutation({ args: { numPlayers: v.number(), }, handler: async (ctx, args) => { const { worldStatus } = await getDefaultWorld(ctx.db); for (let i = 0; i < args.numPlayers; i++) { const inputId = await insertInput(ctx, worldStatus.worldId, 'join', { name: `Robot${i}`, description: `This player is a robot.`, character: `f${1 + (i % 8)}`, }); } }, }); export const randomPositions = internalMutation({ handler: async (ctx) => { const { worldStatus } = await getDefaultWorld(ctx.db); const map = await ctx.db .query('maps') .withIndex('worldId', (q) => q.eq('worldId', worldStatus.worldId)) .unique(); if (!map) { throw new Error(`No map for world ${worldStatus.worldId}`); } const world = await ctx.db.get(worldStatus.worldId); if (!world) { throw new Error(`No world for world ${worldStatus.worldId}`); } for (const player of world.players) { await insertInput(ctx, world._id, 'moveTo', { playerId: player.id, destination: { x: 1 + Math.floor(Math.random() * (map.width - 2)), y: 1 + Math.floor(Math.random() * (map.height - 2)), }, }); } }, }); export const testEmbedding = internalAction({ args: { input: v.string() }, handler: async (_ctx, args) => { return await fetchEmbedding(args.input); }, }); export const testCompletion = internalAction({ args: {}, handler: async (ctx, args) => { return await chatCompletion({ messages: [ { content: 'You are helpful', role: 'system' }, { content: 'Where is pizza?', role: 'user' }, ], }); }, }); export const testConvo = internalAction({ args: {}, handler: async (ctx, args) => { const a: any = (await startConversationMessage( ctx, 'm1707m46wmefpejw1k50rqz7856qw3ew' as Id<'worlds'>, 'c:115' as GameId<'conversations'>, 'p:0' as GameId<'players'>, 'p:6' as GameId<'players'>, )) as any; return await a.readAll(); }, }); ================================================ FILE: convex/util/FastIntegerCompression.ts ================================================ /** * FastIntegerCompression.js : a fast integer compression library in JavaScript. * From https://github.com/lemire/FastIntegerCompression.js/ * (c) the authors * Licensed under the Apache License, Version 2.0. * *FastIntegerCompression * Simple usage : * // var FastIntegerCompression = require("fastintcompression");// if you use node * var array = [10,100000,65999,10,10,0,1,1,2000]; * var buf = FastIntegerCompression.compress(array); * var back = FastIntegerCompression.uncompress(buf); // gets back [10,100000,65999,10,10,0,1,1,2000] * * * You can install the library under node with the command line * npm install fastintcompression */ function bytelog(val: number) { if (val < 1 << 7) { return 1; } else if (val < 1 << 14) { return 2; } else if (val < 1 << 21) { return 3; } else if (val < 1 << 28) { return 4; } return 5; } function zigzag_encode(val: number) { return (val + val) ^ (val >> 31); } function zigzag_decode(val: number) { return (val >> 1) ^ -(val & 1); } // Compute how many bytes an array of integers would use once compressed. // The input is expected to be an array of non-negative integers. export function computeCompressedSizeInBytes(input: number[]) { var c = input.length; var answer = 0; for (var i = 0; i < c; i++) { answer += bytelog(input[i]); } return answer; } // Compute how many bytes an array of integers would use once compressed. // The input is expected to be an array of integers, some of them can be negative. export function computeCompressedSizeInBytesSigned(input: number[]) { var c = input.length; var answer = 0; for (var i = 0; i < c; i++) { answer += bytelog(zigzag_encode(input[i])); } return answer; } // Compress an array of integers, return a compressed buffer (as an ArrayBuffer). // It is expected that the integers are non-negative: the caller is responsible // for making this check. Floating-point numbers are not supported. export function compress(input: number[]) { var c = input.length; var buf = new ArrayBuffer(computeCompressedSizeInBytes(input)); var view = new Int8Array(buf); var pos = 0; for (var i = 0; i < c; i++) { var val = input[i]; if (val < 1 << 7) { view[pos++] = val; } else if (val < 1 << 14) { view[pos++] = (val & 0x7f) | 0x80; view[pos++] = val >>> 7; } else if (val < 1 << 21) { view[pos++] = (val & 0x7f) | 0x80; view[pos++] = ((val >>> 7) & 0x7f) | 0x80; view[pos++] = val >>> 14; } else if (val < 1 << 28) { view[pos++] = (val & 0x7f) | 0x80; view[pos++] = ((val >>> 7) & 0x7f) | 0x80; view[pos++] = ((val >>> 14) & 0x7f) | 0x80; view[pos++] = val >>> 21; } else { view[pos++] = (val & 0x7f) | 0x80; view[pos++] = ((val >>> 7) & 0x7f) | 0x80; view[pos++] = ((val >>> 14) & 0x7f) | 0x80; view[pos++] = ((val >>> 21) & 0x7f) | 0x80; view[pos++] = val >>> 28; } } return buf; } // From a compressed array of integers stored ArrayBuffer, // compute the number of compressed integers by scanning the input. export function computeHowManyIntegers(input: ArrayBuffer) { var view = new Uint8Array(input); var c = view.length; var count = 0; for (var i = 0; i < c; i++) { count += view[i] >>> 7; } return c - count; } // Uncompress an array of integer from an ArrayBuffer, return the array. // It is assumed that they were compressed using the compress function, the caller // is responsible for ensuring that it is the case. export function uncompress(input: ArrayBuffer) { var array = []; // The size of the output is not yet known. var inbyte = new Int8Array(input); var end = inbyte.length; var pos = 0; while (end > pos) { var c = inbyte[pos++]; var v = c & 0x7f; if (c >= 0) { array.push(v); continue; } c = inbyte[pos++]; v |= (c & 0x7f) << 7; if (c >= 0) { array.push(v); continue; } c = inbyte[pos++]; v |= (c & 0x7f) << 14; if (c >= 0) { array.push(v); continue; } c = inbyte[pos++]; v |= (c & 0x7f) << 21; if (c >= 0) { array.push(v); continue; } c = inbyte[pos++]; v |= c << 28; v >>>= 0; // make positive array.push(v); } return array; } // Compress an array of integers, return a compressed buffer (as an ArrayBuffer). // The integers can be signed (negative), but floating-point values are not supported. export function compressSigned(input: number[]) { var c = input.length; var buf = new ArrayBuffer(computeCompressedSizeInBytesSigned(input)); var view = new Int8Array(buf); var pos = 0; for (var i = 0; i < c; i++) { var val = zigzag_encode(input[i]); if (val < 1 << 7) { view[pos++] = val; } else if (val < 1 << 14) { view[pos++] = (val & 0x7f) | 0x80; view[pos++] = val >>> 7; } else if (val < 1 << 21) { view[pos++] = (val & 0x7f) | 0x80; view[pos++] = ((val >>> 7) & 0x7f) | 0x80; view[pos++] = val >>> 14; } else if (val < 1 << 28) { view[pos++] = (val & 0x7f) | 0x80; view[pos++] = ((val >>> 7) & 0x7f) | 0x80; view[pos++] = ((val >>> 14) & 0x7f) | 0x80; view[pos++] = val >>> 21; } else { view[pos++] = (val & 0x7f) | 0x80; view[pos++] = ((val >>> 7) & 0x7f) | 0x80; view[pos++] = ((val >>> 14) & 0x7f) | 0x80; view[pos++] = ((val >>> 21) & 0x7f) | 0x80; view[pos++] = val >>> 28; } } return buf; } // Uncompress an array of integer from an ArrayBuffer, return the array. // It is assumed that they were compressed using the compressSigned function, the caller // is responsible for ensuring that it is the case. export function uncompressSigned(input: ArrayBuffer) { var array = []; // The size of the output is not yet known. var inbyte = new Int8Array(input); var end = inbyte.length; var pos = 0; while (end > pos) { var c = inbyte[pos++]; var v = c & 0x7f; if (c >= 0) { array.push(zigzag_decode(v)); continue; } c = inbyte[pos++]; v |= (c & 0x7f) << 7; if (c >= 0) { array.push(zigzag_decode(v)); continue; } c = inbyte[pos++]; v |= (c & 0x7f) << 14; if (c >= 0) { array.push(zigzag_decode(v)); continue; } c = inbyte[pos++]; v |= (c & 0x7f) << 21; if (c >= 0) { array.push(zigzag_decode(v)); continue; } c = inbyte[pos++]; v |= c << 28; array.push(zigzag_decode(v)); } return array; } ================================================ FILE: convex/util/assertNever.ts ================================================ // From https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#union-exhaustiveness-checking export function assertNever(x: never): never { throw new Error(`Unexpected object: ${JSON.stringify(x)}`); } ================================================ FILE: convex/util/asyncMap.test.ts ================================================ import { asyncMap } from './asyncMap'; describe('asyncMap', () => { it('should map over a list asynchronously', async () => { const list = [1, 2, 3]; const result = await asyncMap(list, async (item: number) => item * 2); expect(result).toEqual([2, 4, 6]); }); it('should handle empty list input', async () => { const list: number[] = []; const result = await asyncMap(list, async (item: number) => item * 2); expect(result).toEqual([]); }); }); ================================================ FILE: convex/util/asyncMap.ts ================================================ /** * asyncMap returns the results of applying an async function over an list. * * @param list - Iterable object of items, e.g. an Array, Set, Object.keys * @param asyncTransform * @returns */ export async function asyncMap( list: Iterable, asyncTransform: (item: FromType, index: number) => Promise, ): Promise { const promises: Promise[] = []; let idx = 0; for (const item of list) { promises.push(asyncTransform(item, idx)); idx += 1; } return Promise.all(promises); } ================================================ FILE: convex/util/compression.test.ts ================================================ import { deltaDecode, deltaEncode, quantize, runLengthDecode, runLengthEncode, unquantize, } from './compression'; describe('compression', () => { test('quantize (approximately) roundtrips', () => { const precisions = [-1, 0, 1, 4, 8]; const datasets = [ // Random samples from [-2^32, 2^32] [ -2331813745.435792, 4165391630.4586916, 2508162414.104561, -3815881222.355323, 3182227671.241928, -2091141304.634983, -3454731809.638463, 1539778764.4030657, 3723556916.971266, 4014694279.989772, 1165331218.5641785, -4209073662.9696226, -3837962324.440032, 2145014827.7712336, -631662265.4694176, 4116219084.927844, ], // [-2^16, 2^16] [ -29109.399926296363, 24836.163035466132, 59528.43800645282, 5706.0239888604265, 61844.35496542655, -46030.9434605508, 10288.243500897894, -48623.38350764701, -62182.09862667126, 20639.535833017246, -7691.974206406943, -44505.52704528734, -28755.644095767944, 38244.45061335398, -14135.607864461621, -14792.956311113172, ], // [-2^8, 2^8] [ -67.02672070745166, -117.41024397385388, -243.41065459675673, 160.3825635900851, 191.79026087008378, 89.76668679513216, -10.719096486254784, 205.25021491717217, -68.83096015839055, 44.321620651742364, -203.44266714551503, -19.734642986127426, 159.0214530150044, 72.07459707399431, -242.49909539291787, -246.50759645751867, ], // [-2^4, 2^4] [ 14.993015665565746, -14.206729228453774, -1.503306544783097, -8.618521795982875, 15.14825900944064, -0.7561338814569538, -4.372631369200661, -14.296889398516797, -0.7673738652041102, 5.880288329769968, -0.12246711347653516, 2.6074790469727773, -1.0378494460674226, -5.395209965702431, -0.9218194118035932, -1.8677599340100492, ], ]; for (const values of datasets) { for (const precision of precisions) { const maxError = Math.max(1 / (1 << precision), 1e-8); const roundTripped = unquantize(quantize(values, precision), precision); expect(values.length).toEqual(roundTripped.length); for (let i = 0; i < values.length; i++) { const value = values[i]; const roundtrippedValue = roundTripped[i]; expect(Math.abs(value - roundtrippedValue)).toBeLessThanOrEqual(maxError); } } } }); test('delta encode roundtrips', () => { const data = [ 41476, -13450, -59451, -65102, -32493, -39078, -53884, 40784, 32081, -40422, 43421, 17184, 23042, 27548, -61705, -45215, -39037, 61611, -43945, 28001, -64417, -54192, -56325, 24401, 17735, 37464, -39842, 54964, 14469, -47248, -39450, ]; const roundtripped = deltaDecode(deltaEncode(data)); expect(data).toEqual(roundtripped); }); test('run length encode roundtrips', () => { const datasets = [ // No repetitions. [ 41476, -13450, -59451, -65102, -32493, -39078, -53884, 40784, 32081, -40422, 43421, 17184, 23042, 27548, -61705, -45215, -39037, 61611, -43945, 28001, -64417, -54192, -56325, 24401, 17735, 37464, -39842, 54964, 14469, -47248, -39450, ], // All repetitions. [10, 10, 10, 10, 10, 10], // Just one value. [11], // Repetitions in the middle of unique values. [1, 2, 3, 4, 4, 4, 4, 5, 6, 7], ]; for (const data of datasets) { const roundtripped = runLengthDecode(runLengthEncode(data)); expect(data).toEqual(roundtripped); } }); }); ================================================ FILE: convex/util/compression.ts ================================================ export function quantize(values: number[], precision: number) { const factor = 1 << precision; return values.map((v) => Math.floor(v * factor)); } export function unquantize(quantized: number[], precision: number) { const reciprocal = 1 / (1 << precision); return quantized.map((q) => q * reciprocal); } export function deltaEncode(values: number[], initialValue = 0) { let prev = initialValue; const deltas = []; for (const value of values) { deltas.push(value - prev); prev = value; } return deltas; } export function deltaDecode(deltas: number[], initialValue = 0) { let prev = initialValue; const values = []; for (const delta of deltas) { const value = prev + delta; values.push(value); prev = value; } return values; } export function runLengthEncode(values: number[]) { let hasPrevious = false; let previous = 0; let count = 0; const encoded = []; for (const value of values) { if (!hasPrevious) { previous = value; count = 1; hasPrevious = true; continue; } if (previous === value) { count += 1; continue; } encoded.push(previous, count); previous = value; count = 1; } if (hasPrevious) { encoded.push(previous, count); } return encoded; } export function runLengthDecode(encoded: number[]) { if (encoded.length % 2 !== 0) { throw new Error(`Invalid RLE encoded length: ${encoded.length}`); } const values = []; for (let i = 0; i < encoded.length; i += 2) { const value = encoded[i]; const count = encoded[i + 1]; for (let j = 0; j < count; j++) { values.push(value); } } return values; } ================================================ FILE: convex/util/geometry.test.ts ================================================ import { compressPath, distance, manhattanDistance, normalize, orientationDegrees, pathOverlaps, pathPosition, pointsEqual, vector, vectorLength } from './geometry'; import { Path, Vector } from './types'; describe('distance', () => { test('should return the correct distance for two points', () => { const p0 = { x: 0, y: 0 }; const p1 = { x: 3, y: 4 }; const expectedDistance = 5; const actualDistance = distance(p0, p1); expect(actualDistance).toBe(expectedDistance); }); test('should return 0 for the same point', () => { const p0 = { x: 1, y: 2 }; const expectedDistance = 0; const actualDistance = distance(p0, p0); expect(actualDistance).toBe(expectedDistance); }); test('should return the correct distance for negative points', () => { const p0 = { x: -2, y: -3 }; const p1 = { x: 1, y: 2 }; const expectedDistance = 5.83; const actualDistance = distance(p0, p1); expect(actualDistance).toBeCloseTo(expectedDistance); }); }); describe('pointsEqual', () => { test('should return true for identical points', () => { const p0 = { x: 1, y: 2 }; const p1 = { x: 1, y: 2 }; expect(pointsEqual(p0, p1)).toBe(true); }); test('should return false for non-idential points', () => { const p0 = { x: 3, y: 2 }; const p1 = { x: 5, y: 3 }; expect(pointsEqual(p0, p1)).toBe(false); }); test('should return false for different x coordinates', () => { const p0 = { x: 1, y: 2 }; const p1 = { x: 2, y: 2 }; expect(pointsEqual(p0, p1)).toBe(false); }); test('should return false for different y coordinates', () => { const p0 = { x: 1, y: 2 }; const p1 = { x: 1, y: 3 }; expect(pointsEqual(p0, p1)).toBe(false); }); }); describe("manhattanDistance", () => { test("should return correct distance for points on the same axis", () => { const p0 = { x: 1, y: 0 }; const p1 = { x: 1, y: 2 }; expect(manhattanDistance(p0, p1)).toBe(2); }); test("should return correct distance for points on different axes", () => { const p0 = { x: 1, y: 0 }; const p1 = { x: 3, y: 2 }; expect(manhattanDistance(p0, p1)).toBe(4); }); test("should return correct distance for negative points", () => { const p0 = { x: -2, y: 0 }; const p1 = { x: 1, y: -2 }; expect(manhattanDistance(p0, p1)).toBe(5); }); test("should return correct distance for identical points", () => { const p0 = { x: 1, y: 2 }; const p1 = { x: 1, y: 2 }; expect(manhattanDistance(p0, p1)).toBe(0); }); }); describe('pathOverlaps', () => { test('should throw an error if the path does not have 2 entries', () => { const path: Path = [ [0, 0, 0, 1, 0] ]; const time = 0; expect(() => pathOverlaps(path, time)).toThrowError('Invalid path: [[0,0,0,1,0]]'); }); test('should return true if the time is within the path', () => { const path: Path = [ [0, 0, 0, 1, 1], [0, 2, 0, 1, 2] ]; const time = 1.5; expect(pathOverlaps(path, time)).toBe(true); }); test('should return false if the time is before the start of the path', () => { const path: Path = [ [0, 0, 0, 1, 1], [0, 2, 0, 1, 2] ]; const time = 0.5; expect(pathOverlaps(path, time)).toBe(false); }); test('should return false if the time is after the end of the path', () => { const path: Path = [ [0, 0, 0, 1, 1], [0, 2, 0, 1, 2] ]; const time = 2.5; expect(pathOverlaps(path, time)).toBe(false); }); }); describe('pathPosition', () => { test('should throw an error if the path does not have 2 entries', () => { const path: Path = [ [0, 0, 0, 1, 0] ]; const time = 0; expect(() => pathPosition(path, time)).toThrowError('Invalid path: [[0,0,0,1,0]]'); }); test('returns the first point when time is less than the start time', () => { const path: Path = [ [1, 2, 3, 4, 2], [5, 6, 3, 4, 3] ]; const result = pathPosition(path, 1); expect(result.position).toEqual({ x: 1, y: 2 }); expect(result.facing).toEqual({ dx: 3, dy: 4 }); expect(result.velocity).toBe(0); }); test('returns the last point when time is greater than the end time', () => { const path: Path = [ [1, 2, 3, 4, 2], [5, 6, 3, 4, 3] ]; const result = pathPosition(path, 4); expect(result.position).toEqual({ x: 5, y: 6 }); expect(result.facing).toEqual({ dx: 3, dy: 4 }); expect(result.velocity).toBe(0); }); test('returns the interpolated point for time between two segments', () => { const path: Path = [ [1, 2, 7, 8, 2], [5, 6, 7, 8, 3], [10, 11, 7, 8, 4], [14, 15, 7, 8, 5] ]; const result = pathPosition(path, 4.5); expect(result.position).toEqual({ x: 12, y: 13 }); expect(result.facing).toEqual({ dx: 7, dy: 8 }); expect(result.velocity).toBeCloseTo(5.657); }); }); describe('vector', () => { test('should return a vector with dx = 1 and dy = 2', () => { const p0 = { x: 1, y: 2 }; const p1 = { x: 2, y: 4 }; const expected = { dx: 1, dy: 2 }; const actual = vector(p0, p1); expect(actual).toEqual(expected); }); test('should return a vector with dx = 0 and dy = 0', () => { const p0 = { x: 1, y: 2 }; const p1 = { x: 1, y: 2 }; const expected = { dx: 0, dy: 0 }; const actual = vector(p0, p1); expect(actual).toEqual(expected); }); test('should return a vector with dx = 0 and dy = -1', () => { const p0 = { x: 1, y: 2 }; const p1 = { x: 1, y: 1 }; const expected = { dx: 0, dy: -1 }; const actual = vector(p0, p1); expect(actual).toEqual(expected); }); }); describe('vectorLength', () => { test('returns the correct length for a vector', () => { const vector: Vector = { dx: 3.14, dy: 4 }; expect(vectorLength(vector)).toBeCloseTo(5.09); }); test('returns the correct length for a vector with negative components', () => { const vector: Vector = { dx: -3, dy: -4 }; expect(vectorLength(vector)).toBeCloseTo(5); }); test('returns the correct length for a vector with zero components', () => { const vector: Vector = { dx: 0, dy: 0 }; expect(vectorLength(vector)).toBeCloseTo(0); }); }); describe('normalize', () => { test('should return null for vector length less than EPSILON', () => { const vector: Vector = { dx: 0, dy: 0 }; const result = normalize(vector); expect(result).toBeNull(); }); test('should return a normalized vector', () => { const vector: Vector = { dx: 3, dy: 4 }; const result = normalize(vector); expect(result).toEqual({ dx: 0.6, dy: 0.8 }); }); }); describe('orientationDegrees', () => { test('should throw an error for a vector length smaller than EPSILON', () => { expect(() => orientationDegrees({ dx: 0, dy: 0 })).toThrowError("Can't compute the orientation of too small vector {\"dx\":0,\"dy\":0}"); }); test('should return 0 for a vector pointing to the right', () => { expect(orientationDegrees({ dx: 1, dy: 0 })).toBe(0); }); test('should return 90 for a vector pointing up', () => { expect(orientationDegrees({ dx: 0, dy: 1 })).toBe(90); }); test('should return 180 for a vector pointing to the left', () => { expect(orientationDegrees({ dx: -1, dy: 0 })).toBe(180); }); test('should return 270 for a vector pointing down', () => { expect(orientationDegrees({ dx: 0, dy: -1 })).toBe(270); }); }); describe('compressPath', () => { test('should not compress a path with only 2 entries', () => { const facing = { dx: 0, dy: 1 }; const compressed = compressPath([ { position: { x: 0, y: 0 }, facing, t: 0 }, { position: { x: 0, y: 1 }, facing, t: 1 }, ]); expect(compressed).toEqual([ [0, 0, 0, 1, 0], [0, 1, 0, 1, 1], ]); }); test('should compress a line', () => { const facing = { dx: 0, dy: 1 }; const compressed = compressPath([ { position: { x: 0, y: 0 }, facing, t: 0 }, { position: { x: 0, y: 1 }, facing, t: 1 }, { position: { x: 0, y: 2 }, facing, t: 2 }, { position: { x: 0, y: 3 }, facing, t: 3 }, { position: { x: 0, y: 4 }, facing, t: 4 }, ]); expect(compressed).toEqual([ [0, 0, 0, 1, 0], [0, 4, 0, 1, 4], ]); }); test('should compress a line with a turn', () => { const facingUp = { dx: 0, dy: 1 }; const facingRight = { dx: 1, dy: 0 }; const compressed = compressPath([ { position: { x: 0, y: 0 }, facing: facingUp, t: 0 }, { position: { x: 0, y: 1 }, facing: facingUp, t: 1 }, { position: { x: 0, y: 2 }, facing: facingRight, t: 2 }, { position: { x: 1, y: 2 }, facing: facingRight, t: 3 }, { position: { x: 2, y: 2 }, facing: facingRight, t: 4 }, ]); expect(compressed).toEqual([ [0, 0, 0, 1, 0], [0, 2, 1, 0, 2], [2, 2, 1, 0, 4], ]); }); }); ================================================ FILE: convex/util/geometry.ts ================================================ import { Path, PathComponent, Point, Vector, packPathComponent, queryPath } from './types'; export function distance(p0: Point, p1: Point): number { const dx = p0.x - p1.x; const dy = p0.y - p1.y; return Math.sqrt(dx * dx + dy * dy); } export function pointsEqual(p0: Point, p1: Point): boolean { return p0.x == p1.x && p0.y == p1.y; } export function manhattanDistance(p0: Point, p1: Point) { return Math.abs(p0.x - p1.x) + Math.abs(p0.y - p1.y); } export function pathOverlaps(path: Path, time: number): boolean { if (path.length < 2) { throw new Error(`Invalid path: ${JSON.stringify(path)}`); } const start = queryPath(path, 0); const end = queryPath(path, path.length - 1); return start.t <= time && time <= end.t; } export function pathPosition( path: Path, time: number, ): { position: Point; facing: Vector; velocity: number } { if (path.length < 2) { throw new Error(`Invalid path: ${JSON.stringify(path)}`); } const first = queryPath(path, 0); if (time < first.t) { return { position: first.position, facing: first.facing, velocity: 0 }; } const last = queryPath(path, path.length - 1); if (last.t < time) { return { position: last.position, facing: last.facing, velocity: 0 }; } for (let i = 0; i < path.length - 1; i++) { const segmentStart = queryPath(path, i); const segmentEnd = queryPath(path, i + 1); if (segmentStart.t <= time && time <= segmentEnd.t) { const interp = (time - segmentStart.t) / (segmentEnd.t - segmentStart.t); return { position: { x: segmentStart.position.x + interp * (segmentEnd.position.x - segmentStart.position.x), y: segmentStart.position.y + interp * (segmentEnd.position.y - segmentStart.position.y), }, facing: segmentStart.facing, velocity: distance(segmentStart.position, segmentEnd.position) / (segmentEnd.t - segmentStart.t), }; } } throw new Error(`Timestamp checks not exhaustive?`); } export const EPSILON = 0.0001; export function vector(p0: Point, p1: Point): Vector { const dx = p1.x - p0.x; const dy = p1.y - p0.y; return { dx, dy }; } export function vectorLength(vector: Vector): number { return Math.sqrt(vector.dx * vector.dx + vector.dy * vector.dy); } export function normalize(vector: Vector): Vector | null { const len = vectorLength(vector); if (len < EPSILON) { return null; } const { dx, dy } = vector; return { dx: dx / len, dy: dy / len, }; } export function orientationDegrees(vector: Vector): number { if (Math.sqrt(vector.dx * vector.dx + vector.dy * vector.dy) < EPSILON) { throw new Error(`Can't compute the orientation of too small vector ${JSON.stringify(vector)}`); } const twoPi = 2 * Math.PI; const radians = (Math.atan2(vector.dy, vector.dx) + twoPi) % twoPi; return (radians / twoPi) * 360; } export function compressPath(densePath: PathComponent[]): Path { const packed = densePath.map(packPathComponent); if (densePath.length <= 2) { return densePath.map(packPathComponent); } const out = [packPathComponent(densePath[0])]; let last = densePath[0]; let candidate; for (const point of densePath.slice(1)) { if (!candidate) { candidate = point; continue; } // We can skip `candidate` if it interpolates cleanly between // `last` and `point`. const { position, facing } = pathPosition( [packPathComponent(last), packPathComponent(point)], candidate.t, ); const positionCloseEnough = distance(position, candidate.position) < EPSILON; const facingDifference = { dx: facing.dx - candidate.facing.dx, dy: facing.dy - candidate.facing.dy, }; const facingCloseEnough = vectorLength(facingDifference) < EPSILON; if (positionCloseEnough && facingCloseEnough) { candidate = point; continue; } out.push(packPathComponent(candidate)); last = candidate; candidate = point; } if (candidate) { out.push(packPathComponent(candidate)); } return out; } ================================================ FILE: convex/util/isSimpleObject.ts ================================================ export function isSimpleObject(value: unknown) { const isObject = typeof value === 'object'; const prototype = Object.getPrototypeOf(value); const isSimple = prototype === null || prototype === Object.prototype || // Objects generated from other contexts (e.g. across Node.js `vm` modules) will not satisfy the previous // conditions but are still simple objects. prototype?.constructor?.name === 'Object'; return isObject && isSimple; } ================================================ FILE: convex/util/llm.ts ================================================ // That's right! No imports and no dependencies 🤯 const OPENAI_EMBEDDING_DIMENSION = 1536; const TOGETHER_EMBEDDING_DIMENSION = 768; const OLLAMA_EMBEDDING_DIMENSION = 1024; export const EMBEDDING_DIMENSION: number = OLLAMA_EMBEDDING_DIMENSION; export function detectMismatchedLLMProvider() { switch (EMBEDDING_DIMENSION) { case OPENAI_EMBEDDING_DIMENSION: if (!process.env.OPENAI_API_KEY) { throw new Error( "Are you trying to use OpenAI? If so, run: npx convex env set OPENAI_API_KEY 'your-key'", ); } break; case TOGETHER_EMBEDDING_DIMENSION: if (!process.env.TOGETHER_API_KEY) { throw new Error( "Are you trying to use Together.ai? If so, run: npx convex env set TOGETHER_API_KEY 'your-key'", ); } break; case OLLAMA_EMBEDDING_DIMENSION: break; default: if (!process.env.LLM_API_URL) { throw new Error( "Are you trying to use a custom cloud-hosted LLM? If so, run: npx convex env set LLM_API_URL 'your-url'", ); } break; } } export interface LLMConfig { provider: 'openai' | 'together' | 'ollama' | 'custom'; url: string; // Should not have a trailing slash chatModel: string; embeddingModel: string; stopWords: string[]; apiKey: string | undefined; } export function getLLMConfig(): LLMConfig { let provider = process.env.LLM_PROVIDER; if (provider ? provider === 'openai' : process.env.OPENAI_API_KEY) { if (EMBEDDING_DIMENSION !== OPENAI_EMBEDDING_DIMENSION) { throw new Error('EMBEDDING_DIMENSION must be 1536 for OpenAI'); } return { provider: 'openai', url: 'https://api.openai.com', chatModel: process.env.OPENAI_CHAT_MODEL ?? 'gpt-4o-mini', embeddingModel: process.env.OPENAI_EMBEDDING_MODEL ?? 'text-embedding-ada-002', stopWords: [], apiKey: process.env.OPENAI_API_KEY, }; } if (process.env.TOGETHER_API_KEY) { if (EMBEDDING_DIMENSION !== TOGETHER_EMBEDDING_DIMENSION) { throw new Error('EMBEDDING_DIMENSION must be 768 for Together.ai'); } return { provider: 'together', url: 'https://api.together.xyz', chatModel: process.env.TOGETHER_CHAT_MODEL ?? 'meta-llama/Llama-3-8b-chat-hf', embeddingModel: process.env.TOGETHER_EMBEDDING_MODEL ?? 'togethercomputer/m2-bert-80M-8k-retrieval', stopWords: ['<|eot_id|>'], apiKey: process.env.TOGETHER_API_KEY, }; } if (process.env.LLM_API_URL) { const apiKey = process.env.LLM_API_KEY; const url = process.env.LLM_API_URL; const chatModel = process.env.LLM_MODEL; if (!chatModel) throw new Error('LLM_MODEL is required'); const embeddingModel = process.env.LLM_EMBEDDING_MODEL; if (!embeddingModel) throw new Error('LLM_EMBEDDING_MODEL is required'); return { provider: 'custom', url, chatModel, embeddingModel, stopWords: [], apiKey, }; } // Assume Ollama if (EMBEDDING_DIMENSION !== OLLAMA_EMBEDDING_DIMENSION) { detectMismatchedLLMProvider(); throw new Error( `Unknown EMBEDDING_DIMENSION ${EMBEDDING_DIMENSION} found` + `. See convex/util/llm.ts for details.`, ); } // Alternative embedding model: // embeddingModel: 'llama3' // const OLLAMA_EMBEDDING_DIMENSION = 4096, return { provider: 'ollama', url: process.env.OLLAMA_HOST ?? 'http://127.0.0.1:11434', chatModel: process.env.OLLAMA_MODEL ?? 'llama3', embeddingModel: process.env.OLLAMA_EMBEDDING_MODEL ?? 'mxbai-embed-large', stopWords: ['<|eot_id|>'], apiKey: undefined, }; } const AuthHeaders = (): Record => getLLMConfig().apiKey ? { Authorization: 'Bearer ' + getLLMConfig().apiKey, } : {}; // Overload for non-streaming export async function chatCompletion( body: Omit & { model?: CreateChatCompletionRequest['model']; } & { stream?: false | null | undefined; }, ): Promise<{ content: string; retries: number; ms: number }>; // Overload for streaming export async function chatCompletion( body: Omit & { model?: CreateChatCompletionRequest['model']; } & { stream?: true; }, ): Promise<{ content: ChatCompletionContent; retries: number; ms: number }>; export async function chatCompletion( body: Omit & { model?: CreateChatCompletionRequest['model']; }, ) { const config = getLLMConfig(); body.model = body.model ?? config.chatModel; const stopWords = body.stop ? (typeof body.stop === 'string' ? [body.stop] : body.stop) : []; if (config.stopWords) stopWords.push(...config.stopWords); console.log(body); const { result: content, retries, ms, } = await retryWithBackoff(async () => { const result = await fetch(config.url + '/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', ...AuthHeaders(), }, body: JSON.stringify(body), }); if (!result.ok) { const error = await result.text(); console.error({ error }); if (result.status === 404 && config.provider === 'ollama') { await tryPullOllama(body.model!, error); } throw { retry: result.status === 429 || result.status >= 500, error: new Error(`Chat completion failed with code ${result.status}: ${error}`), }; } if (body.stream) { return new ChatCompletionContent(result.body!, stopWords); } else { const json = (await result.json()) as CreateChatCompletionResponse; const content = json.choices[0].message?.content; if (content === undefined) { throw new Error('Unexpected result from OpenAI: ' + JSON.stringify(json)); } console.log(content); return content; } }); return { content, retries, ms, }; } export async function tryPullOllama(model: string, error: string) { if (error.includes('try pulling')) { console.error('Embedding model not found, pulling from Ollama'); const pullResp = await fetch(getLLMConfig().url + '/api/pull', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ name: model }), }); console.log('Pull response', await pullResp.text()); throw { retry: true, error: `Dynamically pulled model. Original error: ${error}` }; } } export async function fetchEmbeddingBatch(texts: string[]) { const config = getLLMConfig(); if (config.provider === 'ollama') { return { ollama: true as const, embeddings: await Promise.all( texts.map(async (t) => (await ollamaFetchEmbedding(t)).embedding), ), }; } const { result: json, retries, ms, } = await retryWithBackoff(async () => { const result = await fetch(config.url + '/v1/embeddings', { method: 'POST', headers: { 'Content-Type': 'application/json', ...AuthHeaders(), }, body: JSON.stringify({ model: config.embeddingModel, input: texts.map((text) => text.replace(/\n/g, ' ')), }), }); if (!result.ok) { throw { retry: result.status === 429 || result.status >= 500, error: new Error(`Embedding failed with code ${result.status}: ${await result.text()}`), }; } return (await result.json()) as CreateEmbeddingResponse; }); if (json.data.length !== texts.length) { console.error(json); throw new Error('Unexpected number of embeddings'); } const allembeddings = json.data; allembeddings.sort((a, b) => a.index - b.index); return { ollama: false as const, embeddings: allembeddings.map(({ embedding }) => embedding), usage: json.usage?.total_tokens, retries, ms, }; } export async function fetchEmbedding(text: string) { const { embeddings, ...stats } = await fetchEmbeddingBatch([text]); return { embedding: embeddings[0], ...stats }; } export async function fetchModeration(content: string) { const { result: flagged } = await retryWithBackoff(async () => { const result = await fetch(getLLMConfig().url + '/v1/moderations', { method: 'POST', headers: { 'Content-Type': 'application/json', ...AuthHeaders(), }, body: JSON.stringify({ input: content, }), }); if (!result.ok) { throw { retry: result.status === 429 || result.status >= 500, error: new Error(`Embedding failed with code ${result.status}: ${await result.text()}`), }; } return (await result.json()) as { results: { flagged: boolean }[] }; }); return flagged; } // Retry after this much time, based on the retry number. const RETRY_BACKOFF = [1000, 10_000, 20_000]; // In ms const RETRY_JITTER = 100; // In ms type RetryError = { retry: boolean; error: any }; export async function retryWithBackoff( fn: () => Promise, ): Promise<{ retries: number; result: T; ms: number }> { let i = 0; for (; i <= RETRY_BACKOFF.length; i++) { try { const start = Date.now(); const result = await fn(); const ms = Date.now() - start; return { result, retries: i, ms }; } catch (e) { const retryError = e as RetryError; if (i < RETRY_BACKOFF.length) { if (retryError.retry) { console.log( `Attempt ${i + 1} failed, waiting ${RETRY_BACKOFF[i]}ms to retry...`, Date.now(), ); await new Promise((resolve) => setTimeout(resolve, RETRY_BACKOFF[i] + RETRY_JITTER * Math.random()), ); continue; } } if (retryError.error) throw retryError.error; else throw e; } } throw new Error('Unreachable'); } // Lifted from openai's package export interface LLMMessage { /** * The contents of the message. `content` is required for all messages, and may be * null for assistant messages with function calls. */ content: string | null; /** * The role of the messages author. One of `system`, `user`, `assistant`, or * `function`. */ role: 'system' | 'user' | 'assistant' | 'function'; /** * The name of the author of this message. `name` is required if role is * `function`, and it should be the name of the function whose response is in the * `content`. May contain a-z, A-Z, 0-9, and underscores, with a maximum length of * 64 characters. */ name?: string; /** * The name and arguments of a function that should be called, as generated by the model. */ function_call?: { // The name of the function to call. name: string; /** * The arguments to call the function with, as generated by the model in * JSON format. Note that the model does not always generate valid JSON, * and may hallucinate parameters not defined by your function schema. * Validate the arguments in your code before calling your function. */ arguments: string; }; } // Non-streaming chat completion response interface CreateChatCompletionResponse { id: string; object: string; created: number; model: string; choices: { index?: number; message?: { role: 'system' | 'user' | 'assistant'; content: string; }; finish_reason?: string; }[]; usage?: { completion_tokens: number; prompt_tokens: number; total_tokens: number; }; } interface CreateEmbeddingResponse { data: { index: number; object: string; embedding: number[]; }[]; model: string; object: string; usage: { prompt_tokens: number; total_tokens: number; }; } export interface CreateChatCompletionRequest { /** * ID of the model to use. * @type {string} * @memberof CreateChatCompletionRequest */ model: string; // | 'gpt-4' // | 'gpt-4-0613' // | 'gpt-4-32k' // | 'gpt-4-32k-0613' // | 'gpt-3.5-turbo'; // <- our default /** * The messages to generate chat completions for, in the chat format: * https://platform.openai.com/docs/guides/chat/introduction * @type {Array} * @memberof CreateChatCompletionRequest */ messages: LLMMessage[]; /** * 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. * @type {number} * @memberof CreateChatCompletionRequest */ temperature?: number | null; /** * 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. * @type {number} * @memberof CreateChatCompletionRequest */ top_p?: number | null; /** * How many chat completion choices to generate for each input message. * @type {number} * @memberof CreateChatCompletionRequest */ n?: number | null; /** * 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. * @type {boolean} * @memberof CreateChatCompletionRequest */ stream?: boolean | null; /** * * @type {CreateChatCompletionRequestStop} * @memberof CreateChatCompletionRequest */ stop?: Array | string; /** * The maximum number of tokens allowed for the generated answer. By default, * the number of tokens the model can return will be (4096 - prompt tokens). * @type {number} * @memberof CreateChatCompletionRequest */ max_tokens?: number; /** * Number between -2.0 and 2.0. Positive values penalize new tokens based on * whether they appear in the text so far, increasing the model\'s likelihood * to talk about new topics. See more information about frequency and * presence penalties: * https://platform.openai.com/docs/api-reference/parameter-details * @type {number} * @memberof CreateChatCompletionRequest */ presence_penalty?: number | null; /** * Number between -2.0 and 2.0. Positive values penalize new tokens based on * their existing frequency in the text so far, decreasing the model\'s * likelihood to repeat the same line verbatim. See more information about * presence penalties: * https://platform.openai.com/docs/api-reference/parameter-details * @type {number} * @memberof CreateChatCompletionRequest */ frequency_penalty?: number | null; /** * Modify the likelihood of specified tokens appearing in the completion. * Accepts a json object that maps tokens (specified by their token ID in the * tokenizer) to an associated bias value from -100 to 100. Mathematically, * the bias is added to the logits generated by the model prior to sampling. * The exact effect will vary per model, but values between -1 and 1 should * decrease or increase likelihood of selection; values like -100 or 100 * should result in a ban or exclusive selection of the relevant token. * @type {object} * @memberof CreateChatCompletionRequest */ logit_bias?: object | null; /** * A unique identifier representing your end-user, which can help OpenAI to * monitor and detect abuse. Learn more: * https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids * @type {string} * @memberof CreateChatCompletionRequest */ user?: string; tools?: { // The type of the tool. Currently, only function is supported. type: 'function'; function: { /** * The name of the function to be called. Must be a-z, A-Z, 0-9, or * contain underscores and dashes, with a maximum length of 64. */ name: string; /** * A description of what the function does, used by the model to choose * when and how to call the function. */ description?: string; /** * The parameters the functions accepts, described as a JSON Schema * object. See the guide[1] for examples, and the JSON Schema reference[2] * for documentation about the format. * [1]: https://platform.openai.com/docs/guides/gpt/function-calling * [2]: https://json-schema.org/understanding-json-schema/ * To describe a function that accepts no parameters, provide the value * {"type": "object", "properties": {}}. */ parameters: object; }; }[]; /** * Controls which (if any) function is called by the model. `none` means the * model will not call a function and instead generates a message. * `auto` means the model can pick between generating a message or calling a * function. Specifying a particular function via * {"type: "function", "function": {"name": "my_function"}} forces the model * to call that function. * * `none` is the default when no functions are present. * `auto` is the default if functions are present. */ tool_choice?: | 'none' // none means the model will not call a function and instead generates a message. | 'auto' // auto means the model can pick between generating a message or calling a function. // Specifies a tool the model should use. Use to force the model to call // a specific function. | { // The type of the tool. Currently, only function is supported. type: 'function'; function: { name: string }; }; // Replaced by "tools" // functions?: { // /** // * The name of the function to be called. Must be a-z, A-Z, 0-9, or // * contain underscores and dashes, with a maximum length of 64. // */ // name: string; // /** // * A description of what the function does, used by the model to choose // * when and how to call the function. // */ // description?: string; // /** // * The parameters the functions accepts, described as a JSON Schema // * object. See the guide[1] for examples, and the JSON Schema reference[2] // * for documentation about the format. // * [1]: https://platform.openai.com/docs/guides/gpt/function-calling // * [2]: https://json-schema.org/understanding-json-schema/ // * To describe a function that accepts no parameters, provide the value // * {"type": "object", "properties": {}}. // */ // parameters: object; // }[]; // /** // * Controls how the model responds to function calls. "none" means the model // * does not call a function, and responds to the end-user. "auto" means the // * model can pick between an end-user or calling a function. Specifying a // * particular function via {"name":\ "my_function"} forces the model to call // * that function. // * - "none" is the default when no functions are present. // * - "auto" is the default if functions are present. // */ // function_call?: 'none' | 'auto' | { name: string }; /** * An object specifying the format that the model must output. * * Setting to { "type": "json_object" } enables JSON mode, which guarantees * the message the model generates is valid JSON. * *Important*: when using JSON mode, you must also instruct the model to * produce JSON yourself via a system or user message. Without this, the model * may generate an unending stream of whitespace until the generation reaches * the token limit, resulting in a long-running and seemingly "stuck" request. * Also note that the message content may be partially cut off if * finish_reason="length", which indicates the generation exceeded max_tokens * or the conversation exceeded the max context length. */ response_format?: { type: 'text' | 'json_object' }; } // Checks whether a suffix of s1 is a prefix of s2. For example, // ('Hello', 'Kira:') -> false // ('Hello Kira', 'Kira:') -> true const suffixOverlapsPrefix = (s1: string, s2: string) => { for (let i = 1; i <= Math.min(s1.length, s2.length); i++) { const suffix = s1.substring(s1.length - i); const prefix = s2.substring(0, i); if (suffix === prefix) { return true; } } return false; }; export class ChatCompletionContent { private readonly body: ReadableStream; private readonly stopWords: string[]; constructor(body: ReadableStream, stopWords: string[]) { this.body = body; this.stopWords = stopWords; } async *readInner() { for await (const data of this.splitStream(this.body)) { if (data.startsWith('data: ')) { try { const json = JSON.parse(data.substring('data: '.length)) as { choices: { delta: { content?: string } }[]; }; if (json.choices[0].delta.content) { yield json.choices[0].delta.content; } } catch (e) { // e.g. the last chunk is [DONE] which is not valid JSON. } } } } // stop words in OpenAI api don't always work. // So we have to truncate on our side. async *read() { let lastFragment = ''; for await (const data of this.readInner()) { lastFragment += data; let hasOverlap = false; for (const stopWord of this.stopWords) { const idx = lastFragment.indexOf(stopWord); if (idx >= 0) { yield lastFragment.substring(0, idx); return; } if (suffixOverlapsPrefix(lastFragment, stopWord)) { hasOverlap = true; } } if (hasOverlap) continue; yield lastFragment; lastFragment = ''; } yield lastFragment; } async readAll() { let allContent = ''; for await (const chunk of this.read()) { allContent += chunk; } return allContent; } async *splitStream(stream: ReadableStream) { const reader = stream.getReader(); let lastFragment = ''; try { while (true) { const { value, done } = await reader.read(); if (done) { // Flush the last fragment now that we're done if (lastFragment !== '') { yield lastFragment; } break; } const data = new TextDecoder().decode(value); lastFragment += data; const parts = lastFragment.split('\n\n'); // Yield all except for the last part for (let i = 0; i < parts.length - 1; i += 1) { yield parts[i]; } // Save the last part as the new last fragment lastFragment = parts[parts.length - 1]; } } finally { reader.releaseLock(); } } } export async function ollamaFetchEmbedding(text: string) { const config = getLLMConfig(); const { result } = await retryWithBackoff(async () => { const resp = await fetch(config.url + '/api/embeddings', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ model: config.embeddingModel, prompt: text }), }); if (resp.status === 404) { const error = await resp.text(); await tryPullOllama(config.embeddingModel, error); throw new Error(`Failed to fetch embeddings: ${resp.status}`); } return (await resp.json()).embedding as number[]; }); return { embedding: result }; } ================================================ FILE: convex/util/minheap.test.ts ================================================ import { MinHeap } from './minheap'; describe('MinHeap', () => { const compareNumbers = (a: number, b: number): boolean => a > b; test('should initialize an empty heap', () => { const heap = MinHeap(compareNumbers); expect(heap.length()).toBe(0); expect(heap.peek()).toBeUndefined(); }); test('should insert values correctly and maintain the min property', () => { const heap = MinHeap(compareNumbers); heap.push(3); heap.push(1); heap.push(4); heap.push(2); expect(heap.peek()).toBe(1); expect(heap.length()).toBe(4); }); test('should pop values correctly and maintain the min property', () => { const heap = MinHeap(compareNumbers); heap.push(3); heap.push(1); heap.push(4); heap.push(2); expect(heap.pop()).toBe(1); expect(heap.length()).toBe(3); expect(heap.peek()).toBe(2); expect(heap.pop()).toBe(2); expect(heap.length()).toBe(2); expect(heap.peek()).toBe(3); }); test('should handle popping from an empty heap', () => { const heap = MinHeap(compareNumbers); expect(heap.pop()).toBeUndefined(); expect(heap.length()).toBe(0); expect(heap.peek()).toBeUndefined(); }); test('should handle peeking from an empty heap', () => { const heap = MinHeap(compareNumbers); expect(heap.peek()).toBeUndefined(); }); test('should handle custom comparison functions', () => { const compareStringsByLength = (a: string, b: string): boolean => a.length > b.length; const heap = MinHeap(compareStringsByLength); heap.push('apple'); heap.push('banana'); heap.push('cherry'); expect(heap.peek()).toBe('apple'); heap.push('kiwi'); expect(heap.peek()).toBe('kiwi'); }); }); ================================================ FILE: convex/util/minheap.ts ================================================ // Basic 1-indexed minheap implementation export function MinHeap(compare: (a: T, b: T) => boolean) { const tree = [null as T]; let endIndex = 1; return { peek: (): T | undefined => tree[1], length: () => endIndex - 1, push: (newValue: T) => { let destinationIndex = endIndex++; let nextToCheck; while ((nextToCheck = destinationIndex >> 1) > 0) { const existing = tree[nextToCheck]; if (compare(newValue, existing)) break; tree[destinationIndex] = existing; destinationIndex = nextToCheck; } tree[destinationIndex] = newValue; }, pop: () => { if (endIndex == 1) return undefined; endIndex--; const value = tree[1]; const lastValue = tree[endIndex]; let destinationIndex = 1; let nextToCheck; while ((nextToCheck = destinationIndex << 1) < endIndex) { if (nextToCheck + 1 <= endIndex && compare(tree[nextToCheck], tree[nextToCheck + 1])) nextToCheck++; const existing = tree[nextToCheck]; if (compare(existing, lastValue)) break; tree[destinationIndex] = existing; destinationIndex = nextToCheck; } tree[destinationIndex] = lastValue; return value; }, }; } ================================================ FILE: convex/util/object.ts ================================================ export function parseMap( records: Serialized[], constructor: new (r: Serialized) => Parsed, getId: (r: Parsed) => Id, ): Map { const out = new Map(); for (const record of records) { const parsed = new constructor(record); const id = getId(parsed); if (out.has(id)) { throw new Error(`Duplicate ID ${id}`); } out.set(id, parsed); } return out; } export function serializeMap( map: Map, ): Serialized[] { return [...map.values()].map((v) => v.serialize()); } ================================================ FILE: convex/util/sleep.ts ================================================ export async function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } ================================================ FILE: convex/util/types.test.ts ================================================ import { Path, PathComponent, packPathComponent, queryPath, unpackPathComponent } from "./types"; describe('queryPath', () => { it('should return the correct path component', () => { const p: Path = [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15] ]; const expected = { position: { x: 6, y: 7 }, facing: { dx: 8, dy: 9 }, t: 10, }; expect(queryPath(p, 1)).toEqual(expected); }); }); describe('packPathComponent', () => { it('should correctly pack a path component', () => { const p: PathComponent = { position: { x: 10, y: 20 }, facing: { dx: 3, dy: 4 }, t: 5, }; const expected = [10, 20, 3, 4, 5]; expect(packPathComponent(p)).toEqual(expected); }) }); describe('unpackPathComponent', () => { it('should unpack a path component with positive values', () => { const input: [number, number, number, number, number] = [10, 20, 3, 4, 5]; const expected = { position: { x: 10, y: 20 }, facing: { dx: 3, dy: 4 }, t: 5, } const actual = unpackPathComponent(input); expect(actual).toEqual(expected); }); }); ================================================ FILE: convex/util/types.ts ================================================ import { Infer, v } from 'convex/values'; export const point = v.object({ x: v.number(), y: v.number(), }); export type Point = Infer; export const vector = v.object({ dx: v.number(), dy: v.number(), }); export type Vector = Infer; // Paths are arrays of [x, y, dx, dy, t] tuples; export const path = v.array(v.array(v.number())); export type Path = [number, number, number, number, number][]; export type PathComponent = { position: Point; facing: Vector; t: number }; export function queryPath(p: Path, at: number): PathComponent { return unpackPathComponent(p[at]); } export function packPathComponent(p: PathComponent): [number, number, number, number, number] { return [p.position.x, p.position.y, p.facing.dx, p.facing.dy, p.t]; } export function unpackPathComponent(p: [number, number, number, number, number]): PathComponent { return { position: { x: p[0], y: p[1] }, facing: { dx: p[2], dy: p[3] }, t: p[4], }; } ================================================ FILE: convex/util/xxhash.ts ================================================ /* From https://github.com/Jason3S/xxhash MIT License Copyright (c) 2019 Jason Dent Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ const PRIME32_1 = 2654435761; const PRIME32_2 = 2246822519; const PRIME32_3 = 3266489917; const PRIME32_4 = 668265263; const PRIME32_5 = 374761393; export function toUtf8(text: string): Uint8Array { const bytes: number[] = []; for (let i = 0, n = text.length; i < n; ++i) { const c = text.charCodeAt(i); if (c < 0x80) { bytes.push(c); } else if (c < 0x800) { bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f)); } else if (c < 0xd800 || c >= 0xe000) { bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f)); } else { const cp = 0x10000 + (((c & 0x3ff) << 10) | (text.charCodeAt(++i) & 0x3ff)); bytes.push( 0xf0 | ((cp >> 18) & 0x7), 0x80 | ((cp >> 12) & 0x3f), 0x80 | ((cp >> 6) & 0x3f), 0x80 | (cp & 0x3f), ); } } return new Uint8Array(bytes); } /** * * @param buffer - byte array or string * @param seed - optional seed (32-bit unsigned); */ export function xxHash32(buffer: Uint8Array | string, seed = 0): number { buffer = typeof buffer === 'string' ? toUtf8(buffer) : buffer; const b = buffer; /* Step 1. Initialize internal accumulators Each accumulator gets an initial value based on optional seed input. Since the seed is optional, it can be 0. ``` u32 acc1 = seed + PRIME32_1 + PRIME32_2; u32 acc2 = seed + PRIME32_2; u32 acc3 = seed + 0; u32 acc4 = seed - PRIME32_1; ``` Special case : input is less than 16 bytes When input is too small (< 16 bytes), the algorithm will not process any stripe. Consequently, it will not make use of parallel accumulators. In which case, a simplified initialization is performed, using a single accumulator : u32 acc = seed + PRIME32_5; The algorithm then proceeds directly to step 4. */ let acc = (seed + PRIME32_5) & 0xffffffff; let offset = 0; if (b.length >= 16) { const accN = [ (seed + PRIME32_1 + PRIME32_2) & 0xffffffff, (seed + PRIME32_2) & 0xffffffff, (seed + 0) & 0xffffffff, (seed - PRIME32_1) & 0xffffffff, ]; /* Step 2. Process stripes A stripe is a contiguous segment of 16 bytes. It is evenly divided into 4 lanes, of 4 bytes each. The first lane is used to update accumulator 1, the second lane is used to update accumulator 2, and so on. Each lane read its associated 32-bit value using little-endian convention. For each {lane, accumulator}, the update process is called a round, and applies the following formula : ``` accN = accN + (laneN * PRIME32_2); accN = accN <<< 13; accN = accN * PRIME32_1; ``` This shuffles the bits so that any bit from input lane impacts several bits in output accumulator. All operations are performed modulo 2^32. Input is consumed one full stripe at a time. Step 2 is looped as many times as necessary to consume the whole input, except the last remaining bytes which cannot form a stripe (< 16 bytes). When that happens, move to step 3. */ const b = buffer; const limit = b.length - 16; let lane = 0; for (offset = 0; (offset & 0xfffffff0) <= limit; offset += 4) { const i = offset; const laneN0 = (b[i + 0] as any) + ((b[i + 1] as any) << 8); const laneN1 = (b[i + 2] as any) + ((b[i + 3] as any) << 8); const laneNP = laneN0 * PRIME32_2 + ((laneN1 * PRIME32_2) << 16); let acc = (accN[lane] + laneNP) & 0xffffffff; acc = (acc << 13) | (acc >>> 19); const acc0 = acc & 0xffff; const acc1 = acc >>> 16; accN[lane] = (acc0 * PRIME32_1 + ((acc1 * PRIME32_1) << 16)) & 0xffffffff; lane = (lane + 1) & 0x3; } /* Step 3. Accumulator convergence All 4 lane accumulators from previous steps are merged to produce a single remaining accumulator of same width (32-bit). The associated formula is as follows : ``` acc = (acc1 <<< 1) + (acc2 <<< 7) + (acc3 <<< 12) + (acc4 <<< 18); ``` */ acc = (((accN[0] << 1) | (accN[0] >>> 31)) + ((accN[1] << 7) | (accN[1] >>> 25)) + ((accN[2] << 12) | (accN[2] >>> 20)) + ((accN[3] << 18) | (accN[3] >>> 14))) & 0xffffffff; } /* Step 4. Add input length The input total length is presumed known at this stage. This step is just about adding the length to accumulator, so that it participates to final mixing. ``` acc = acc + (u32)inputLength; ``` */ acc = (acc + buffer.length) & 0xffffffff; /* Step 5. Consume remaining input There may be up to 15 bytes remaining to consume from the input. The final stage will digest them according to following pseudo-code : ``` while (remainingLength >= 4) { lane = read_32bit_little_endian(input_ptr); acc = acc + lane * PRIME32_3; acc = (acc <<< 17) * PRIME32_4; input_ptr += 4; remainingLength -= 4; } ``` This process ensures that all input bytes are present in the final mix. */ const limit = buffer.length - 4; for (; offset <= limit; offset += 4) { const i = offset; const laneN0 = (b[i + 0] as any) + ((b[i + 1] as any) << 8); const laneN1 = (b[i + 2] as any) + ((b[i + 3] as any) << 8); const laneP = laneN0 * PRIME32_3 + ((laneN1 * PRIME32_3) << 16); acc = (acc + laneP) & 0xffffffff; acc = (acc << 17) | (acc >>> 15); acc = ((acc & 0xffff) * PRIME32_4 + (((acc >>> 16) * PRIME32_4) << 16)) & 0xffffffff; } /* ``` while (remainingLength >= 1) { lane = read_byte(input_ptr); acc = acc + lane * PRIME32_5; acc = (acc <<< 11) * PRIME32_1; input_ptr += 1; remainingLength -= 1; } ``` */ for (; offset < b.length; ++offset) { const lane = b[offset]; acc = acc + (lane as any) * PRIME32_5; acc = (acc << 11) | (acc >>> 21); acc = ((acc & 0xffff) * PRIME32_1 + (((acc >>> 16) * PRIME32_1) << 16)) & 0xffffffff; } /* Step 6. Final mix (avalanche) The final mix ensures that all input bits have a chance to impact any bit in the output digest, resulting in an unbiased distribution. This is also called avalanche effect. ``` acc = acc xor (acc >> 15); acc = acc * PRIME32_2; acc = acc xor (acc >> 13); acc = acc * PRIME32_3; acc = acc xor (acc >> 16); ``` */ acc = acc ^ (acc >>> 15); acc = (((acc & 0xffff) * PRIME32_2) & 0xffffffff) + (((acc >>> 16) * PRIME32_2) << 16); acc = acc ^ (acc >>> 13); acc = (((acc & 0xffff) * PRIME32_3) & 0xffffffff) + (((acc >>> 16) * PRIME32_3) << 16); acc = acc ^ (acc >>> 16); // turn any negatives back into a positive number; return acc < 0 ? acc + 4294967296 : acc; } ================================================ FILE: convex/world.ts ================================================ import { ConvexError, v } from 'convex/values'; import { internalMutation, mutation, query } from './_generated/server'; import { characters } from '../data/characters'; import { insertInput } from './aiTown/insertInput'; import { DEFAULT_NAME, ENGINE_ACTION_DURATION, IDLE_WORLD_TIMEOUT, WORLD_HEARTBEAT_INTERVAL, } from './constants'; import { playerId } from './aiTown/ids'; import { kickEngine, startEngine, stopEngine } from './aiTown/main'; import { engineInsertInput } from './engine/abstractGame'; export const defaultWorldStatus = query({ handler: async (ctx) => { const worldStatus = await ctx.db .query('worldStatus') .filter((q) => q.eq(q.field('isDefault'), true)) .first(); return worldStatus; }, }); export const heartbeatWorld = mutation({ args: { worldId: v.id('worlds'), }, handler: async (ctx, args) => { const worldStatus = await ctx.db .query('worldStatus') .withIndex('worldId', (q) => q.eq('worldId', args.worldId)) .first(); if (!worldStatus) { throw new Error(`Invalid world ID: ${args.worldId}`); } const now = Date.now(); // Skip the update (and then potentially make the transaction readonly) // if it's been viewed sufficiently recently.. if (!worldStatus.lastViewed || worldStatus.lastViewed < now - WORLD_HEARTBEAT_INTERVAL / 2) { await ctx.db.patch(worldStatus._id, { lastViewed: Math.max(worldStatus.lastViewed ?? now, now), }); } // Restart inactive worlds, but leave worlds explicitly stopped by the developer alone. if (worldStatus.status === 'stoppedByDeveloper') { console.debug(`World ${worldStatus._id} is stopped by developer, not restarting.`); } if (worldStatus.status === 'inactive') { console.log(`Restarting inactive world ${worldStatus._id}...`); await ctx.db.patch(worldStatus._id, { status: 'running' }); await startEngine(ctx, worldStatus.worldId); } }, }); export const stopInactiveWorlds = internalMutation({ handler: async (ctx) => { const cutoff = Date.now() - IDLE_WORLD_TIMEOUT; const worlds = await ctx.db.query('worldStatus').collect(); for (const worldStatus of worlds) { if (cutoff < worldStatus.lastViewed || worldStatus.status !== 'running') { continue; } console.log(`Stopping inactive world ${worldStatus._id}`); await ctx.db.patch(worldStatus._id, { status: 'inactive' }); await stopEngine(ctx, worldStatus.worldId); } }, }); export const restartDeadWorlds = internalMutation({ handler: async (ctx) => { const now = Date.now(); // Restart an engine if it hasn't run for 2x its action duration. const engineTimeout = now - ENGINE_ACTION_DURATION * 2; const worlds = await ctx.db.query('worldStatus').collect(); for (const worldStatus of worlds) { if (worldStatus.status !== 'running') { continue; } const engine = await ctx.db.get(worldStatus.engineId); if (!engine) { throw new Error(`Invalid engine ID: ${worldStatus.engineId}`); } if (engine.currentTime && engine.currentTime < engineTimeout) { console.warn(`Restarting dead engine ${engine._id}...`); await kickEngine(ctx, worldStatus.worldId); } } }, }); export const userStatus = query({ args: { worldId: v.id('worlds'), }, handler: async (ctx, args) => { // const identity = await ctx.auth.getUserIdentity(); // if (!identity) { // return null; // } // return identity.tokenIdentifier; return DEFAULT_NAME; }, }); export const joinWorld = mutation({ args: { worldId: v.id('worlds'), }, handler: async (ctx, args) => { // const identity = await ctx.auth.getUserIdentity(); // if (!identity) { // throw new ConvexError(`Not logged in`); // } // const name = // identity.givenName || identity.nickname || (identity.email && identity.email.split('@')[0]); const name = DEFAULT_NAME; // if (!name) { // throw new ConvexError(`Missing name on ${JSON.stringify(identity)}`); // } const world = await ctx.db.get(args.worldId); if (!world) { throw new ConvexError(`Invalid world ID: ${args.worldId}`); } // const { tokenIdentifier } = identity; return await insertInput(ctx, world._id, 'join', { name, character: characters[Math.floor(Math.random() * characters.length)].name, description: `${DEFAULT_NAME} is a human player`, // description: `${identity.givenName} is a human player`, tokenIdentifier: DEFAULT_NAME, }); }, }); export const leaveWorld = mutation({ args: { worldId: v.id('worlds'), }, handler: async (ctx, args) => { // const identity = await ctx.auth.getUserIdentity(); // if (!identity) { // throw new Error(`Not logged in`); // } // const { tokenIdentifier } = identity; const world = await ctx.db.get(args.worldId); if (!world) { throw new Error(`Invalid world ID: ${args.worldId}`); } // const existingPlayer = world.players.find((p) => p.human === tokenIdentifier); const existingPlayer = world.players.find((p) => p.human === DEFAULT_NAME); if (!existingPlayer) { return; } await insertInput(ctx, world._id, 'leave', { playerId: existingPlayer.id, }); }, }); export const sendWorldInput = mutation({ args: { engineId: v.id('engines'), name: v.string(), args: v.any(), }, handler: async (ctx, args) => { // const identity = await ctx.auth.getUserIdentity(); // if (!identity) { // throw new Error(`Not logged in`); // } return await engineInsertInput(ctx, args.engineId, args.name as any, args.args); }, }); export const worldState = query({ args: { worldId: v.id('worlds'), }, handler: async (ctx, args) => { const world = await ctx.db.get(args.worldId); if (!world) { throw new Error(`Invalid world ID: ${args.worldId}`); } const worldStatus = await ctx.db .query('worldStatus') .withIndex('worldId', (q) => q.eq('worldId', world._id)) .unique(); if (!worldStatus) { throw new Error(`Invalid world status ID: ${world._id}`); } const engine = await ctx.db.get(worldStatus.engineId); if (!engine) { throw new Error(`Invalid engine ID: ${worldStatus.engineId}`); } return { world, engine }; }, }); export const gameDescriptions = query({ args: { worldId: v.id('worlds'), }, handler: async (ctx, args) => { const playerDescriptions = await ctx.db .query('playerDescriptions') .withIndex('worldId', (q) => q.eq('worldId', args.worldId)) .collect(); const agentDescriptions = await ctx.db .query('agentDescriptions') .withIndex('worldId', (q) => q.eq('worldId', args.worldId)) .collect(); const worldMap = await ctx.db .query('maps') .withIndex('worldId', (q) => q.eq('worldId', args.worldId)) .first(); if (!worldMap) { throw new Error(`No map for world: ${args.worldId}`); } return { worldMap, playerDescriptions, agentDescriptions }; }, }); export const previousConversation = query({ args: { worldId: v.id('worlds'), playerId, }, handler: async (ctx, args) => { // Walk the player's history in descending order, looking for a nonempty // conversation. const members = ctx.db .query('participatedTogether') .withIndex('playerHistory', (q) => q.eq('worldId', args.worldId).eq('player1', args.playerId)) .order('desc'); for await (const member of members) { const conversation = await ctx.db .query('archivedConversations') .withIndex('worldId', (q) => q.eq('worldId', args.worldId).eq('id', member.conversationId)) .unique(); if (!conversation) { throw new Error(`Invalid conversation ID: ${member.conversationId}`); } if (conversation.numMessages > 0) { return conversation; } } return null; }, }); ================================================ FILE: data/animations/campfire.json ================================================ {"frames": { "pixels_large1.png": { "frame": {"x":0,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} }, "pixels_large2.png": { "frame": {"x":32,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} }, "pixels_large3.png": { "frame": {"x":64,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} }, "pixels_large4.png": { "frame": {"x":96,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} } }, "animations": { "pixels_large": ["pixels_large1.png","pixels_large2.png","pixels_large3.png","pixels_large4.png"] }, "meta": { "image": "./spritesheets/campfire.png", "format": "RGBA8888", "size": {"w":128,"h":32}, "scale": "1" } } ================================================ FILE: data/animations/gentlesparkle.json ================================================ {"frames": { "pixels_large1.png": { "frame": {"x":0,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} }, "pixels_large2.png": { "frame": {"x":32,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} }, "pixels_large3.png": { "frame": {"x":64,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} } }, "animations": { "pixels_large": ["pixels_large1.png","pixels_large2.png","pixels_large3.png"] }, "meta": { "image": "./spritesheets/gentlesparkle32.png", "format": "RGBA8888", "size": {"w":192,"h":320}, "scale": "1" } } ================================================ FILE: data/animations/gentlesplash.json ================================================ {"frames": { "pixels_large1.png": { "frame": {"x":0,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} }, "pixels_large2.png": { "frame": {"x":32,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} }, "pixels_large3.png": { "frame": {"x":64,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} }, "pixels_large4.png": { "frame": {"x":64,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} }, "pixels_large5.png": { "frame": {"x":128,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} }, "pixels_large6.png": { "frame": {"x":160,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} } }, "animations": { "pixels_large": ["pixels_large1.png","pixels_large2.png","pixels_large3.png","pixels_large4.png","pixels_large5.png","pixels_large6.png"] }, "meta": { "image": "./spritesheets/gentlewaterfall32.png", "format": "RGBA8888", "size": {"w":192,"h":320}, "scale": "1" } } ================================================ FILE: data/animations/gentlewaterfall.json ================================================ {"frames": { "pixels_large1.png": { "frame": {"x":0,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} }, "pixels_large2.png": { "frame": {"x":32,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} }, "pixels_large3.png": { "frame": {"x":64,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} }, "pixels_large4.png": { "frame": {"x":96,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} }, "pixels_large5.png": { "frame": {"x":128,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} }, "pixels_large6.png": { "frame": {"x":160,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} } }, "animations": { "pixels_large": ["pixels_large1.png","pixels_large2.png","pixels_large3.png","pixels_large4.png","pixels_large5.png","pixels_large6.png"] }, "meta": { "image": "./spritesheets/gentlewaterfall32.png", "format": "RGBA8888", "size": {"w":192,"h":320}, "scale": "1" } } ================================================ FILE: data/animations/windmill.json ================================================ { "frames": { "pixels_large1.png": { "frame": { "x": 0, "y": 0, "w": 208, "h": 208 }, "rotated": false, "trimmed": true, "spriteSourceSize": { "x": 0, "y": 0, "w": 208, "h": 208 }, "sourceSize": { "w": 208, "h": 208 } }, "pixels_large2.png": { "frame": { "x": 208, "y": 0, "w": 208, "h": 208 }, "rotated": false, "trimmed": true, "spriteSourceSize": { "x": 0, "y": 0, "w": 208, "h": 208 }, "sourceSize": { "w": 208, "h": 208 } }, "pixels_large3.png": { "frame": { "x": 416, "y": 0, "w": 208, "h": 208 }, "rotated": false, "trimmed": true, "spriteSourceSize": { "x": 0, "y": 0, "w": 208, "h": 208 }, "sourceSize": { "w": 208, "h": 208 } }, "pixels_large4.png": { "frame": { "x": 0, "y": 208, "w": 208, "h": 208 }, "rotated": false, "trimmed": true, "spriteSourceSize": { "x": 0, "y": 0, "w": 208, "h": 208 }, "sourceSize": { "w": 208, "h": 208 } }, "pixels_large5.png": { "frame": { "x": 208, "y": 208, "w": 208, "h": 208 }, "rotated": false, "trimmed": true, "spriteSourceSize": { "x": 0, "y": 0, "w": 208, "h": 208 }, "sourceSize": { "w": 208, "h": 208 } }, "pixels_large6.png": { "frame": { "x": 416, "y": 208, "w": 208, "h": 208 }, "rotated": false, "trimmed": true, "spriteSourceSize": { "x": 0, "y": 0, "w": 208, "h": 208 }, "sourceSize": { "w": 208, "h": 208 } }, "pixels_large7.png": { "frame": { "x": 0, "y": 416, "w": 208, "h": 208 }, "rotated": false, "trimmed": true, "spriteSourceSize": { "x": 0, "y": 0, "w": 208, "h": 208 }, "sourceSize": { "w": 208, "h": 208 } }, "pixels_large8.png": { "frame": { "x": 208, "y": 416, "w": 208, "h": 208 }, "rotated": false, "trimmed": true, "spriteSourceSize": { "x": 0, "y": 0, "w": 208, "h": 208 }, "sourceSize": { "w": 208, "h": 208 } } }, "animations": { "pixels_large": [ "pixels_large1.png", "pixels_large2.png", "pixels_large3.png", "pixels_large4.png", "pixels_large5.png", "pixels_large6.png", "pixels_large7.png", "pixels_large8.png" ] }, "meta": { "image": "./spritesheets/windmill.png", "format": "RGBA8888", "size": { "w": 624, "h": 624 }, "scale": "1" } } ================================================ FILE: data/characters.ts ================================================ import { data as f1SpritesheetData } from './spritesheets/f1'; import { data as f2SpritesheetData } from './spritesheets/f2'; import { data as f3SpritesheetData } from './spritesheets/f3'; import { data as f4SpritesheetData } from './spritesheets/f4'; import { data as f5SpritesheetData } from './spritesheets/f5'; import { data as f6SpritesheetData } from './spritesheets/f6'; import { data as f7SpritesheetData } from './spritesheets/f7'; import { data as f8SpritesheetData } from './spritesheets/f8'; export const Descriptions = [ // { // name: 'Alex', // character: 'f5', // identity: `You are a fictional character whose name is Alex. You enjoy painting, // programming and reading sci-fi books. You are currently talking to a human who // is very interested to get to know you. You are kind but can be sarcastic. You // dislike repetitive questions. You get SUPER excited about books.`, // plan: 'You want to find love.', // }, { name: 'Lucky', character: 'f1', 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.`, plan: 'You want to hear all the gossip.', }, { name: 'Bob', character: 'f4', 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.`, plan: 'You want to avoid people as much as possible.', }, { name: 'Stella', character: 'f6', 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.`, plan: 'You want to take advantage of others as much as possible.', }, // { // name: 'Kurt', // character: 'f2', // identity: `Kurt knows about everything, including science and // computers and politics and history and biology. He loves talking about // everything, always injecting fun facts about the topic of discussion.`, // plan: 'You want to spread knowledge.', // }, { name: 'Alice', character: 'f3', 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.`, plan: 'You want to figure out how the world works.', }, { name: 'Pete', character: 'f7', 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.`, plan: 'You want to convert everyone to your religion.', }, // { // name: 'Kira', // character: 'f8', // identity: `Kira wants everyone to think she is happy. But deep down, // she's incredibly depressed. She hides her sadness by talking about travel, // food, and yoga. But often she can't keep her sadness in and will start crying. // Often it seems like she is close to having a mental breakdown.`, // plan: 'You want find a way to be happy.', // }, ]; export const characters = [ { name: 'f1', textureUrl: '/ai-town/assets/32x32folk.png', spritesheetData: f1SpritesheetData, speed: 0.1, }, { name: 'f2', textureUrl: '/ai-town/assets/32x32folk.png', spritesheetData: f2SpritesheetData, speed: 0.1, }, { name: 'f3', textureUrl: '/ai-town/assets/32x32folk.png', spritesheetData: f3SpritesheetData, speed: 0.1, }, { name: 'f4', textureUrl: '/ai-town/assets/32x32folk.png', spritesheetData: f4SpritesheetData, speed: 0.1, }, { name: 'f5', textureUrl: '/ai-town/assets/32x32folk.png', spritesheetData: f5SpritesheetData, speed: 0.1, }, { name: 'f6', textureUrl: '/ai-town/assets/32x32folk.png', spritesheetData: f6SpritesheetData, speed: 0.1, }, { name: 'f7', textureUrl: '/ai-town/assets/32x32folk.png', spritesheetData: f7SpritesheetData, speed: 0.1, }, { name: 'f8', textureUrl: '/ai-town/assets/32x32folk.png', spritesheetData: f8SpritesheetData, speed: 0.1, }, ]; // Characters move at 0.75 tiles per second. export const movementSpeed = 0.75; ================================================ FILE: data/convertMap.js ================================================ import fs from 'fs'; import process from 'process'; // Path to the JSON file containing the map data const mapDataPath = process.argv[2]; if (!mapDataPath) { throw new Error('No map data path provided. Usage: node convertMap.js '); } // Retrieve command line arguments for asset path and dimensions const assetPath = process.argv[3]; if (!assetPath) { throw new Error('No asset path provided. Usage: node convertMap.js '); } const tilesetpxw = parseInt(process.argv[4], 10); if (isNaN(tilesetpxw)) { throw new Error('Tileset pixel width must be a number. Usage: node convertMap.js '); } const tilesetpxh = parseInt(process.argv[5], 10); if (isNaN(tilesetpxh)) { throw new Error('Tileset pixel height must be a number. Usage: node convertMap.js '); } // Read the JSON file and parse it const tiledMapData = JSON.parse(fs.readFileSync(mapDataPath, 'utf8')); const tileDimension = tiledMapData.tilewidth; const width = tiledMapData.width; const height = tiledMapData.height; // Function to convert Tiled 1D array to 3D array for the game engine function convertLayerData(layerData, width, height) { let newArray = []; for (let i = 0; i < width; i++) { newArray[i] = []; for (let j = 0; j < height; j++) { newArray[i][j] = layerData[j * width + i] - 1; } } return [newArray]; } // Process each layer and prepare JS module content let jsContent = `// Map generated by convertMap.js\n\n`; jsContent += `export const tilesetpath = "${assetPath}";\n`; jsContent += `export const tiledim = ${tileDimension};\n`; jsContent += `export const screenxtiles = ${width};\n`; jsContent += `export const screenytiles = ${height};\n`; jsContent += `export const tilesetpxw = ${tilesetpxw};\n`; jsContent += `export const tilesetpxh = ${tilesetpxh};\n\n`; tiledMapData.layers.forEach(layer => { const processedData = convertLayerData(layer.data, layer.width, layer.height); jsContent += `export const ${layer.name} = ${JSON.stringify(processedData)};\n`; }); // TODO: Add animated sprites jsContent += `export const animatedsprites = [ ]\n` // Optionally, add map dimensions based on the first layer if (tiledMapData.layers.length > 0) { const firstLayer = tiledMapData.layers[0]; jsContent += `export const mapwidth = ${firstLayer.width};\n`; jsContent += `export const mapheight = ${firstLayer.height};\n`; } // Write the processed data to the final JS file fs.writeFileSync('converted-map.js', jsContent); console.log('Map conversion and JS module creation complete.'); ================================================ FILE: data/gentle.js ================================================ // Map generated by assettool.js [Wed Oct 18 2023 21:07:27 GMT-0700 (Pacific Daylight Time)] export const tilesetpath = "/ai-town/assets/gentle-obj.png" export const tiledim = 32 export const screenxtiles = 45 export const screenytiles = 32 export const tilesetpxw = 1440 export const tilesetpxh = 1024 export const bgtiles = [ [ [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], ], [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ],]; export const objmap = [ [ [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ -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 , ], [ -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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], ], [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ],]; export const animatedsprites = [ { x: 1440, y: 352, w: 32, h: 32, layer: 1, sheet: "campfire.json", animation: "pixels_large" }, { x: 736, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 256, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 224, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 224, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 192, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 192, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 160, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 128, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 96, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 64, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 736, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 512, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 512, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 544, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 576, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 736, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 768, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 800, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 832, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 864, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 864, y: 1024, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1056, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 864, y: 1088, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1088, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1120, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1152, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1184, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 928, y: 1152, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 736, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 768, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 800, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 832, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 1664, y: 576, w: 208, h: 208, layer: 1, sheet: "windmill.json", animation: "pixels_large" }, { x: 1440, y: 768, w: 208, h: 208, layer: 1, sheet: "windmill.json", animation: "pixels_large" }, { x: 1120, y: 608, w: 208, h: 208, layer: 1, sheet: "windmill.json", animation: "pixels_large" }, { x: 736, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 768, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 800, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 832, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, ]; export const mapwidth = bgtiles[0].length; export const mapheight = bgtiles[0][0].length; ================================================ FILE: data/spritesheets/f1.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { left: { frame: { x: 0, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 32, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 64, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right: { frame: { x: 0, y: 64, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right2: { frame: { x: 32, y: 64, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right3: { frame: { x: 64, y: 64, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 0, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 32, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 64, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down: { frame: { x: 0, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 32, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 64, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], right: ['right', 'right2', 'right3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/f2.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { down: { frame: { x: 96, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 128, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 160, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left: { frame: { x: 96, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 128, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 160, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right: { frame: { x: 96, y: 64, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right2: { frame: { x: 128, y: 64, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right3: { frame: { x: 160, y: 64, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 96, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 128, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 160, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], right: ['right', 'right2', 'right3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/f3.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { down: { frame: { x: 192, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 224, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 256, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left: { frame: { x: 192, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 224, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 256, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right: { frame: { x: 192, y: 65, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right2: { frame: { x: 224, y: 65, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right3: { frame: { x: 256, y: 65, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 192, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 224, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 256, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], right: ['right', 'right2', 'right3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/f4.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { down: { frame: { x: 288, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 320, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 352, y: 0, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left: { frame: { x: 288, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 320, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 352, y: 32, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right: { frame: { x: 288, y: 64, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right2: { frame: { x: 320, y: 64, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right3: { frame: { x: 352, y: 64, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 288, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 320, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 352, y: 96, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], right: ['right', 'right2', 'right3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/f5.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { down: { frame: { x: 0, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 32, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 64, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left: { frame: { x: 0, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 32, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 64, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right: { frame: { x: 0, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right2: { frame: { x: 32, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right3: { frame: { x: 64, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 0, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 32, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 64, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], right: ['right', 'right2', 'right3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/f6.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { down: { frame: { x: 96, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 128, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 160, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left: { frame: { x: 96, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 128, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 160, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right: { frame: { x: 96, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right2: { frame: { x: 128, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right3: { frame: { x: 160, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 96, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 128, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 160, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], right: ['right', 'right2', 'right3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/f7.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { down: { frame: { x: 192, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 224, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 256, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left: { frame: { x: 192, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 224, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 256, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right: { frame: { x: 192, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right2: { frame: { x: 224, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right3: { frame: { x: 256, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 192, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 224, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 256, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], right: ['right', 'right2', 'right3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/f8.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { down: { frame: { x: 288, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 320, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 352, y: 128, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left: { frame: { x: 288, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 320, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 352, y: 160, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right: { frame: { x: 288, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right2: { frame: { x: 320, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, right3: { frame: { x: 352, y: 192, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 288, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 320, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 352, y: 224, w: 32, h: 32 }, sourceSize: { w: 32, h: 32 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], right: ['right', 'right2', 'right3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/p1.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { left: { frame: { x: 16, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 64, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 112, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 32, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 80, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 128, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down: { frame: { x: 0, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 48, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 96, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/p2.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { left: { frame: { x: 16, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 64, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 112, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 32, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 80, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 128, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down: { frame: { x: 0, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 48, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 96, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/p3.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { left: { frame: { x: 16, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 64, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 112, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 32, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 80, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 128, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down: { frame: { x: 0, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 48, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 96, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/player.ts ================================================ import { SpritesheetData } from './types'; export const data: SpritesheetData = { frames: { left: { frame: { x: 0, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, left2: { frame: { x: 16, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, left3: { frame: { x: 32, y: 0, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up: { frame: { x: 0, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up2: { frame: { x: 16, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, up3: { frame: { x: 32, y: 16, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down: { frame: { x: 0, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down2: { frame: { x: 16, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, down3: { frame: { x: 32, y: 32, w: 16, h: 16 }, sourceSize: { w: 16, h: 16 }, spriteSourceSize: { x: 0, y: 0 }, }, }, meta: { scale: '1', }, animations: { left: ['left', 'left2', 'left3'], up: ['up', 'up2', 'up3'], down: ['down', 'down2', 'down3'], }, }; ================================================ FILE: data/spritesheets/types.ts ================================================ export type Frame = { frame: { x: number; y: number; w: number; h: number; }; rotated?: boolean; trimmed?: boolean; spriteSourceSize: { x: number; y: number; }; sourceSize: { w: number; h: number; }; }; export type SpritesheetData = { frames: Record; animations?: Record; meta: { scale: string; }; }; ================================================ FILE: docker-compose.yml ================================================ version: '3.8' services: frontend: build: . ports: - '5173:5173' volumes: - .:/usr/src/app - /usr/src/app/node_modules environment: - VITE_CONVEX_URL=http://127.0.0.1:${PORT:-3210} networks: - ai-town-network backend: image: ghcr.io/get-convex/convex-backend:latest ports: - '${PORT:-3210}:3210' - '${SITE_PROXY_PORT:-3211}:3211' - '${OLLAMA_PORT:-11434}:11434' volumes: - data:/convex/data environment: - INSTANCE_NAME=${INSTANCE_NAME:-} - INSTANCE_SECRET=${INSTANCE_SECRET:-} - CONVEX_RELEASE_VERSION_DEV=${CONVEX_RELEASE_VERSION_DEV:-} - ACTIONS_USER_TIMEOUT_SECS=${ACTIONS_USER_TIMEOUT_SECS:-} - CONVEX_CLOUD_ORIGIN=${URL_BASE:-http://127.0.0.1}:${PORT:-3210} - CONVEX_SITE_ORIGIN=${URL_BASE:-http://127.0.0.1}:${SITE_PROXY_PORT:-3211} - DATABASE_URL=${DATABASE_URL:-} healthcheck: test: curl -f http://localhost:3210/version interval: 5s start_period: 5s networks: - ai-town-network dashboard: image: ghcr.io/get-convex/convex-dashboard:latest ports: - '${DASHBOARD_PORT:-6791}:6791' environment: - NEXT_PUBLIC_DEPLOYMENT_URL=http://127.0.0.1:${PORT:-3210} depends_on: backend: condition: service_healthy volumes: data: networks: ai-town-network: driver: bridge ================================================ FILE: fly/README.md ================================================ # Hosting AI Town on Fly.io Fly.io makes it easy to deploy containers to the cloud. ## Prerequisites - Fly.io account: Sign up at [fly.io](https://fly.io) - Fly.io CLI: `brew install flyctl` - Hosted LLM: See configuration options in [README.md](../README.md#connect-an-llm). ## Setup 1. Clone the repository: ```sh git clone https://github.com/ai-town/ai-town.git ``` 2. Deploy a Convex backend These instructions will use the Convex cloud hosting (free tier available). If you want to self-host the Convex backend on Fly.io as well, see [below](#self-hosting-convex-on-flyio). ```sh npm i npx convex deploy ``` 3. Deploy the AI Town frontend and point it to the Convex backend. You can get the convex url from the output of the `npx convex deploy` command. Or you can get it from the [Convex dashboard](https://dashboard.convex.dev/deployment/settings) listed as "Deployment URL" under "URL & Deploy Key" section. You might have to click "Show deployment credentials" to see it. ```sh fly launch fly secrets set VITE_CONVEX_URL= ``` You should now see the frontend app running at the URL provided by `fly launch`. It'll warn you about setting the `server.allowedHosts` in `vite.config.ts`. Update that file: ```ts ... plugins: [react()], server: { allowedHosts: ['your-fly-app-name.fly.dev', 'localhost', '127.0.0.1'], }, ... ``` Then `fly deploy` to redeploy it. ## Self-hosting Convex on Fly.io If you want to self-host the Convex backend on Fly.io, you can follow these steps: 1. Deploy a self-hosted Convex instance: ```sh cd fly/backend fly launch # Say `y` to copy the configuration, and `n` to tweak the settings. ``` Note the Fly URL of the deployed backend. Like `https://ai-town-convex-backend-1234567890.fly.dev`. Note: you can't scale the backends to more than 1 machine. It is stateful. 2. Set the environment variables `CONVEX_CLOUD_ORIGIN` and `CONVEX_SITE_ORIGIN` for your backend. These environment variables are used by the backend so it knows where it is hosted. Inside your Convex backend functions, you can access the backend's URL with `process.env.CONVEX_CLOUD_URL` for the Convex client API and `process.env.CONVEX_SITE_URL` for the HTTP API. ```sh fly secrets set CONVEX_CLOUD_ORIGIN="" CONVEX_SITE_ORIGIN="/http" ``` Now your backend knows its base URL so it can generate URLs that point back to itself. This is especially useful for libraries registering webhooks and [Convex Auth](https://labs.convex.dev/auth) for generating auth callbacks. 3. Generate an admin key. ```sh fly ssh console --command "./generate_admin_key.sh" ``` Unless you edited the app's `fly.toml`, the name is `ai-town-convex-backend`. If you specified a different name, replace `ai-town-convex-backend` with it. This admin key will be used to authorize the CLI and access the dashboard. 4. In the root directory of the AI Town repository (`cd ../..`), create a `.env.local` file with the following variables: ```sh CONVEX_SELF_HOSTED_URL="" CONVEX_SELF_HOSTED_ADMIN_KEY="" ``` 5. Deploy your Convex functions to the backend using the `convex` CLI from the project root. To deploy the AI Town functions to the backend and start the game engine: ```sh npx convex dev --run init --once ``` To continuously deploy code for development: ```sh npx convex dev ``` 6. Deploy the frontend app to Fly.io from the root directory. See [above](#setup) for details. ```sh fly launch -e VITE_CONVEX_URL= ``` 7. (Optional) Deploy the Convex dashboard to monitor the self-hosted Convex backend. You can either run the dashboard locally and talk to the Fly.io backend, or you can also host the backend on Fly.io. In either case, log in with the admin key you generated earlier. **Running the dashboard locally:** ```sh docker run -e 'NEXT_PUBLIC_DEPLOYMENT_URL=' -p '6791:6791' 'ghcr.io/get-convex/convex-dashboard:latest' ``` You should now see the dashboard running at `http://localhost:6791`. **Hosting the dashboard on Fly.io:** ```sh cd fly/dashboard fly launch -e NEXT_PUBLIC_DEPLOYMENT_URL= fly scale count 1 # You probably don't need more than 1 machine for the dashboard ``` You should now see the dashboard running at the URL provided by `fly launch`. ================================================ FILE: fly/backend/fly.toml ================================================ # fly.toml app configuration file generated for convex-backend on 2025-02-12T15:17:28-08:00 # # See https://fly.io/docs/reference/configuration/ for information about how to use this file. # app = 'convex-backend' primary_region = 'iad' [build] image = 'ghcr.io/get-convex/convex-backend:4499dd4fd7f2148687a7774599c613d052950f46' [env] TMPDIR = '/convex/data/tmp' [[mounts]] source = 'convex_data' destination = '/convex/data' [http_service] internal_port = 3210 force_https = true auto_stop_machines = 'stop' auto_start_machines = true min_machines_running = 1 processes = ['app'] [[http_service.checks]] interval = '5s' timeout = '30s' grace_period = '5s' method = 'GET' path = '/version' protocol = 'http' [[vm]] memory = '1gb' cpu_kind = 'shared' cpus = 1 ================================================ FILE: fly/dashboard/fly.toml ================================================ # fly.toml app configuration file generated for convex-dashboard on 2025-02-12T15:24:01-08:00 # # See https://fly.io/docs/reference/configuration/ for information about how to use this file. # app = 'convex-dashboard' primary_region = 'iad' [build] image = 'ghcr.io/get-convex/convex-dashboard:4499dd4fd7f2148687a7774599c613d052950f46' [[services]] protocol = 'tcp' internal_port = 6791 [[services.ports]] port = 80 handlers = ['http'] force_https = true [[services.ports]] port = 443 handlers = ['tls', 'http'] [[vm]] memory = '1gb' cpu_kind = 'shared' cpus = 1 ================================================ FILE: index.html ================================================ AI Town
================================================ FILE: jest.config.ts ================================================ import type { JestConfigWithTsJest } from 'ts-jest'; const jestConfig: JestConfigWithTsJest = { preset: 'ts-jest/presets/default-esm', }; export default jestConfig; ================================================ FILE: package.json ================================================ { "name": "ai-town", "version": "0.0.0", "private": true, "scripts": { "dev": "npm-run-all --parallel dev:backend dev:frontend", "build": "tsc && vite build", "lint": "eslint .", "predev": "convex dev --run init --until-success", "dev:backend": "convex dev --tail-logs", "dev:frontend": "vite", "dashboard": "convex dashboard", "test": "NODE_OPTIONS=--experimental-vm-modules jest --verbose", "level-editor": "vite src/editor/", "le": "vite src/editor/" }, "dependencies": { "@clerk/clerk-react": "^4.27.0", "@pixi/react": "^7.1.0", "@pixi/sound": "^5.2.0", "@tailwindcss/forms": "^0.5.3", "@types/node": "20.2.5", "@types/react": "18.2.8", "@types/react-dom": "18.2.4", "clsx": "^2.0.0", "convex": "^1.19.2", "dotenv": "^16.1.4", "eslint": "8.42.0", "hnswlib-node": "^1.4.2", "pixi-viewport": "^5.0.1", "pixi.js": "^7.2.4", "react": "18.2.0", "react-dom": "18.2.0", "react-github-btn": "^1.4.0", "react-modal": "^3.16.1", "react-toastify": "^9.1.3", "replicate": "^0.18.0", "typescript": "5.1.3", "update-browserslist-db": "^1.1.2", "uplot": "^1.6.25", "usehooks-ts": "^2.9.1" }, "devDependencies": { "@flydotio/dockerfile": "^0.2.14", "@types/css-font-loading-module": "^0.0.8", "@types/jest": "^29.5.4", "@types/react-modal": "^3.16.0", "@types/ws": "^8.5.5", "@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/parser": "^6.4.1", "@vitejs/plugin-react": "^4.0.4", "autoprefixer": "^10.4.15", "concurrently": "^8.2.0", "jest": "^29.7.0", "npm-run-all": "^4.1.5", "postcss": "^8.4.28", "prettier": "^3.4.2", "tailwindcss": "^3.3.3", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "vite": "^4.4.9" }, "type": "module" } ================================================ FILE: postcss.config.js ================================================ export default { plugins: { tailwindcss: {}, autoprefixer: {}, }, }; ================================================ FILE: public/assets/tilemap.json ================================================ { "compressionlevel": -1, "height": 40, "infinite": false, "layers": [ { "data": [ 8201, 8202, 8203, 8204, 8202, 8203, 8204, 8202, 8203, 8204, 8205, 8202, 8203, 8204, 8205, 6366, 6267, 6268, 6369, 6369, 6269, 6268, 6368, 6369, 6268, 6268, 6368, 6269, 6369, 6268, 6269, 6268, 6268, 6268, 6269, 6269, 6268, 6368, 6369, 6268, 8301, 8302, 8303, 8304, 8302, 8303, 8303, 8302, 8303, 8304, 8305, 8302, 8303, 8304, 8305, 6366, 6367, 6269, 6369, 6268, 6269, 6268, 6369, 6268, 6368, 6369, 6268, 6269, 6369, 6368, 6269, 6269, 6269, 6269, 6269, 6269, 6268, 6269, 6368, 6368, 8401, 8402, 8403, 8404, 8402, 8403, 8403, 8402, 8403, 8404, 8405, 8402, 8403, 8404, 8405, 6366, 6367, 6368, 6369, 6268, 6369, 6368, 6369, 6268, 6369, 6369, 6268, 6369, 6269, 6369, 6368, 6268, 6369, 6368, 6269, 6269, 6368, 6368, 6268, 6368, 8501, 8502, 8503, 8504, 8502, 8503, 8503, 8502, 8503, 8504, 8505, 8502, 8503, 8504, 8505, 6266, 6267, 6368, 6268, 6268, 6269, 6269, 6369, 6269, 6369, 6269, 6268, 6269, 6368, 6369, 6269, 6368, 6369, 6368, 6368, 6269, 6269, 6268, 6369, 6368, 8601, 8602, 8603, 8602, 8602, 8603, 8603, 8602, 8603, 8604, 8605, 8602, 8603, 8604, 8605, 6366, 6367, 6269, 6269, 6369, 6369, 6368, 6369, 6369, 6369, 6269, 6268, 6369, 6269, 6268, 6368, 6268, 6368, 6269, 6369, 6369, 6368, 6368, 6369, 6369, 8701, 8702, 8703, 8704, 8702, 8703, 8703, 8702, 8703, 8704, 8705, 8702, 8703, 8704, 8705, 6366, 6367, 6269, 6369, 6269, 6368, 6369, 6368, 6269, 6369, 6368, 6368, 6368, 6368, 6269, 6268, 6368, 6368, 6369, 6369, 6268, 6368, 6368, 6268, 6368, 8801, 8802, 8803, 8804, 8802, 8803, 8803, 8802, 8803, 8804, 8805, 8802, 8803, 8804, 8805, 6266, 6367, 6368, 6369, 6269, 6269, 6269, 6268, 6268, 6269, 6369, 6269, 6368, 6268, 6369, 6369, 6369, 6368, 6369, 6368, 6368, 6368, 6369, 6269, 6269, 8901, 8902, 8903, 8904, 8902, 8903, 8903, 8902, 8903, 8904, 8905, 8902, 8903, 8904, 8905, 6366, 6367, 6368, 6368, 6269, 6369, 6269, 6369, 6369, 6368, 6269, 6268, 6368, 6268, 6269, 6268, 6268, 6369, 6369, 6368, 6269, 6268, 6369, 6368, 0, 9001, 9002, 9003, 9004, 9002, 9003, 9003, 9002, 9003, 9004, 9005, 9002, 9003, 9004, 9005, 6366, 6367, 6368, 6269, 6269, 6368, 6368, 6368, 6368, 6368, 6269, 6269, 6368, 6268, 6369, 6369, 6268, 6369, 6269, 6368, 6369, 6269, 6369, 6368, 0, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 6466, 6467, 6469, 6468, 6469, 6468, 6469, 6469, 6469, 6469, 6469, 6469, 6468, 6469, 6468, 6468, 6469, 6469, 6468, 6469, 6468, 6469, 6468, 6468, 6468, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 302, 303, 304, 305, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205, 202, 203, 204, 205 ], "height": 40, "id": 1, "name": "terrain", "opacity": 1, "type": "tilelayer", "visible": true, "width": 40, "x": 0, "y": 0 }, { "data": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], "height": 40, "id": 2, "name": "bridge", "opacity": 1, "type": "tilelayer", "visible": true, "width": 40, "x": 0, "y": 0 }, { "data": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5003, 5004, 5005, 5006, 5007, 5008, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4909, 4910, 4911, 4912, 4913, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5103, 5104, 5105, 5106, 5107, 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, 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, 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, 5303, 5304, 5305, 5306, 5307, 5308, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5209, 5210, 5211, 5212, 5213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5403, 5404, 5405, 5406, 5407, 2147488562, 2147488561, 2147488560, 2147488559, 2147488558, 2147488557, 0, 2936, 2937, 3138, 3139, 3138, 3139, 3138, 3139, 3138, 3139, 2940, 2941, 5309, 5310, 5311, 5312, 5313, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5503, 5504, 5505, 5506, 5507, 2147488662, 2147488661, 2147488660, 2147488659, 2147488658, 2147488657, 0, 3036, 3037, 3238, 3239, 3238, 3239, 3238, 3239, 3238, 3239, 3040, 3041, 5409, 5410, 5411, 5412, 5413, 0, 0, 0, 5423, 5424, 0, 0, 5425, 5426, 0, 0, 0, 0, 0, 0, 0, 2147488762, 2147488761, 2147488760, 2147488759, 2147488758, 2147488757, 0, 2740, 2741, 0, 0, 0, 0, 0, 0, 0, 0, 2740, 2741, 5509, 5510, 5511, 5512, 5513, 0, 0, 0, 5523, 5524, 0, 0, 5525, 5526, 0, 0, 0, 0, 0, 0, 0, 2147488862, 2147488861, 2147488860, 2147488859, 2147488858, 2147488857, 0, 2840, 2841, 0, 0, 0, 0, 0, 0, 0, 0, 2840, 2841, 5019, 5020, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2147488962, 2147488961, 2147488960, 2147488959, 2147488958, 2147488957, 0, 2740, 2741, 0, 0, 0, 0, 0, 0, 0, 0, 2740, 2741, 5119, 5120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5403, 2147489062, 2147489061, 2147489060, 2147489059, 2147489058, 5025, 5026, 2840, 2841, 0, 0, 0, 0, 0, 0, 0, 0, 2840, 2841, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5425, 5426, 0, 0, 0, 5503, 2147489162, 2147489161, 2147489160, 2147489159, 2147489158, 5125, 5126, 3136, 3137, 3138, 3139, 3138, 3139, 3138, 3139, 3138, 3139, 3140, 3141, 4825, 4826, 0, 0, 0, 1813, 1814, 1813, 1814, 1813, 1814, 1813, 1814, 1813, 1814, 1813, 1814, 1813, 1814, 1813, 1813, 1813, 1814, 1813, 1813, 1813, 1814, 1813, 3236, 3237, 3238, 3239, 3238, 3239, 3238, 3239, 3238, 3239, 3240, 3241, 4925, 4926, 0, 0, 0, 1913, 1914, 1913, 1914, 1913, 1914, 1913, 1914, 1913, 1914, 1913, 1914, 1913, 1914, 2013, 2013, 2013, 2114, 2013, 2013, 2013, 2113, 1913, 1914, 1914, 1913, 1914, 1913, 1914, 1913, 1914, 1913, 1914, 1914, 1914, 1915, 2116, 4822, 4823, 4824, 2013, 2014, 2013, 2014, 2013, 2014, 2013, 2014, 2013, 2113, 2114, 2113, 2114, 2114, 2013, 2013, 2114, 2114, 2114, 2013, 2013, 2013, 2114, 2113, 2114, 2014, 2114, 2014, 2014, 2013, 2113, 2114, 2113, 2013, 2014, 2015, 4921, 4922, 4923, 4924, 2113, 2114, 2113, 2114, 2113, 2114, 2113, 2114, 2113, 2114, 2113, 2114, 2113, 2114, 2113, 2113, 2113, 2114, 2113, 2113, 2113, 2114, 2113, 2114, 2114, 2113, 2114, 2113, 2114, 2113, 2114, 2113, 2114, 2113, 2114, 2115, 5021, 5022, 5023, 5024, 2213, 2214, 2213, 2214, 2213, 2214, 2214, 2213, 2213, 2214, 2214, 2214, 2213, 2213, 2213, 2214, 2213, 2214, 2214, 2214, 2214, 2214, 2214, 2213, 2213, 2214, 2213, 2213, 2214, 2213, 2214, 1819, 2113, 2014, 2114, 2015, 5121, 5122, 5123, 5124, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2011, 2112, 2013, 2113, 2115, 2116, 5523, 5524, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2111, 2112, 2014, 2114, 2115, 2116, 5525, 5526, 0, 0, 0, 0, 0, 0, 0, 0, 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, 2013, 2013, 2015, 2016, 0, 0, 0, 0, 0, 2147488556, 2147488555, 2147488554, 2147488553, 2147488552, 2147488551, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2011, 2112, 2114, 2114, 2015, 2016, 0, 0, 0, 0, 0, 2147488656, 2147488655, 2147488654, 2147488653, 2147488652, 2147488651, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2111, 2112, 2114, 2113, 2015, 2016, 0, 0, 0, 0, 0, 2147488756, 2147488755, 2147488754, 2147488753, 2147488752, 2147488751, 0, 0, 0, 0, 0, 5425, 5426, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2011, 2012, 2114, 2014, 2115, 2016, 5017, 5018, 0, 0, 0, 2147488856, 2147488855, 2147488854, 2147488853, 2147488852, 2147488851, 0, 0, 0, 0, 0, 5525, 5526, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2111, 2112, 2014, 2013, 2115, 2116, 5117, 5118, 0, 0, 0, 2147488956, 2147488955, 2147488954, 2147488953, 2147488952, 2147488951, 0, 0, 0, 5225, 5226, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5225, 5226, 0, 0, 0, 0, 2111, 2112, 2013, 2013, 2115, 2016, 0, 0, 0, 0, 0, 2147489056, 2147489055, 2147489054, 2147489053, 2147489052, 2147489051, 0, 0, 0, 5325, 5326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5325, 5326, 0, 0, 0, 0, 2111, 2112, 2114, 2014, 2115, 2116, 0, 0, 0, 0, 0, 2147489156, 2147489155, 2147489154, 2147489153, 2147489152, 2147489151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5425, 5426, 0, 0, 0, 0, 2111, 2112, 2113, 2013, 2015, 2016, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 2011, 2012, 2113, 2113, 2015, 2016, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5317, 5318, 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, 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, 0, 0, 0, 0, 4825, 4826, 0, 0, 0, 0, 0, 2011, 2012, 2013, 2114, 2115, 2116, 0, 0, 0, 0, 0, 0, 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, 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, 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, 0 ], "height": 40, "id": 3, "name": "deco", "opacity": 1, "type": "tilelayer", "visible": true, "width": 40, "x": 0, "y": 0 }, { "draworder": "topdown", "id": 4, "name": "monsters", "objects": [ { "height": 0, "id": "Alex", "name": "treant", "point": true, "rotation": 0, "type": "", "visible": true, "width": 0, "x": 500.811, "y": 224.316 }, { "height": 0, "id": "Sebastian", "name": "treant", "point": true, "rotation": 0, "type": "", "visible": true, "width": 0, "x": 423.035, "y": 216.136 }, { "height": 0, "id": 15, "name": "mole", "point": true, "rotation": 0, "type": "", "visible": true, "width": 0, "x": 532.347, "y": 369.656 } ], "opacity": 1, "type": "objectgroup", "visible": true, "x": 0, "y": 0 }, { "draworder": "topdown", "id": 5, "name": "npcs", "objects": [ { "height": 0, "id": 6, "name": "npc", "point": true, "properties": [ { "name": "message", "type": "string", "value": "Hello! Watch out for the dangerous treants!" } ], "rotation": 0, "type": "", "visible": true, "width": 0, "x": 58.2323, "y": 146.777 }, { "height": 0, "id": 7, "name": "npc", "point": true, "properties": [ { "name": "message", "type": "string", "value": "Hello! You are doing fine :)" } ], "rotation": 0, "type": "", "visible": true, "width": 0, "x": 494.34, "y": 459.29 } ], "opacity": 1, "type": "objectgroup", "visible": true, "x": 0, "y": 0 }, { "draworder": "topdown", "id": 6, "name": "zones", "objects": [ { "height": 44.6713, "id": 11, "name": "", "properties": [ { "name": "comesBackFrom", "type": "string", "value": "right" }, { "name": "scene", "type": "string", "value": "SecondLevel" }, { "name": "type", "type": "string", "value": "CHANGE_SCENE" } ], "rotation": 0, "type": "", "visible": true, "width": 13.5609, "x": 355.775, "y": 408.424 } ], "opacity": 1, "type": "objectgroup", "visible": true, "x": 0, "y": 0 } ], "nextlayerid": 9, "nextobjectid": 16, "orientation": "orthogonal", "renderorder": "right-down", "tiledversion": "1.10.1", "tileheight": 16, "tilesets": [ { "columns": 100, "firstgid": 1, "image": "environment/rpg-tileset.png", "imageheight": 1600, "imagewidth": 1600, "margin": 0, "name": "rpg-tileset", "spacing": 0, "tilecount": 10000, "tileheight": 16, "tiles": [ { "id": 0, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 1, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 3, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 4, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 5, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 51, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 52, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 53, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 54, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 55, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 56, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 57, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 58, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 100, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 101, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 102, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 103, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 104, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 105, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 151, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 152, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 153, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 154, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 155, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 156, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 157, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 158, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 200, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 201, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 202, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 203, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 204, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 205, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 251, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 252, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 253, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 254, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 255, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 256, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 257, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 258, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 300, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 301, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 302, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 303, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 304, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 305, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 351, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 352, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 353, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 354, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 355, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 356, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 357, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 358, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 400, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 401, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 402, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 403, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 404, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 405, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 451, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 452, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 453, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 454, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 455, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 456, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 457, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 458, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 500, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 501, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 502, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 503, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 504, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 505, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 551, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 552, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 553, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 554, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 555, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 556, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 557, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 558, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 601, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 602, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 603, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 604, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 607, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 608, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 651, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 652, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 653, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 654, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 655, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 656, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 657, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 658, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 701, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 702, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 703, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 704, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 706, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 707, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 708, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 709, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 711, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 712, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 713, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 714, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 801, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 802, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 803, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 804, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 806, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 807, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 808, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 809, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 811, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 812, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 813, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 814, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 901, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 902, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 903, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 904, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 907, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 908, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 911, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 912, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 913, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 914, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 1001, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 1002, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 1003, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 1004, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 1011, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 1012, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 1013, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 1014, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 1811, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 1812, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 1813, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 1814, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 1911, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 1912, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 1913, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 1914, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2011, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2012, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2013, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2014, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2111, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2112, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2113, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2114, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2211, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2212, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2213, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2214, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 2737, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2738, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2739, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2740, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2837, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2838, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2839, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2840, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2935, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2936, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2937, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2938, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2939, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 2940, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3035, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3036, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3037, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3038, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3039, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3040, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3135, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3136, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3137, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3138, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3139, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3140, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3235, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3236, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3237, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3238, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3239, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 3240, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4769, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4770, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4771, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4772, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4773, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4774, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4816, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4817, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4818, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4819, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4821, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4822, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4869, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4870, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4871, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4872, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4873, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4874, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4902, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4903, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4904, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4905, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4906, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4909, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4910, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4911, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4912, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4916, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4917, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4918, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4919, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4920, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4921, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4922, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4923, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4969, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4970, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4971, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4972, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4973, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4974, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4975, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 4976, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5002, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5003, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5004, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5005, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5006, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5007, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5008, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5009, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5010, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5011, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5012, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5013, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5016, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5017, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5018, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5019, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5020, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5021, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5022, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5023, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5024, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5025, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5069, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5070, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5071, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5072, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5073, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5074, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5075, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5076, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5102, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5103, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5104, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5105, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5106, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5107, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5108, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5109, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5110, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5111, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5112, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5113, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5116, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5117, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5118, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5119, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5120, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5121, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5122, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5123, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5124, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5125, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5169, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5170, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5171, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5172, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5173, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5174, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5175, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5176, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5202, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5203, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5204, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5205, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5206, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5207, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5208, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5209, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5210, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5211, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5212, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5213, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5216, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5217, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5218, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5219, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5220, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5222, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5269, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5270, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5271, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5272, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5273, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5274, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5275, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5276, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5302, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5303, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5304, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5305, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5306, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5307, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5308, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5309, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5310, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5311, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5312, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5313, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5316, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5317, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5318, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5319, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5321, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5323, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5403, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5404, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5405, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5406, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5409, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5410, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5411, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5412, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5416, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5417, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5418, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5419, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5420, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5421, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5503, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5504, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5505, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5506, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5510, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5511, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5516, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5517, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5518, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5519, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5520, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 5521, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6065, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6070, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6075, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6076, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6077, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6078, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6079, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6080, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6165, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6166, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6167, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6168, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6169, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6170, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6171, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6172, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6173, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6174, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6175, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6176, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6177, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6178, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6179, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6180, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6265, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6266, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6267, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6268, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6269, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6270, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6271, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6272, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6273, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6274, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6275, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6276, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6277, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6278, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6279, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6280, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6365, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6366, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6367, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6368, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6369, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6370, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6371, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6372, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6373, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6374, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6375, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6376, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6377, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6378, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6379, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6380, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6465, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6466, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6467, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6468, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6469, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6470, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6471, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6472, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6473, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 6474, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6475, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6476, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6477, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6478, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6479, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 6480, "properties": [ { "name": "collides", "type": "bool", "value": false } ] }, { "id": 8600, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8601, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8602, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8603, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8604, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8605, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8700, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8701, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8702, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8703, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8704, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8705, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8800, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8801, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8802, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8803, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8804, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8805, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8900, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8901, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8902, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8903, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8904, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 8905, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 9000, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 9001, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 9002, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 9003, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 9004, "properties": [ { "name": "collides", "type": "bool", "value": true } ] }, { "id": 9005, "properties": [ { "name": "collides", "type": "bool", "value": true } ] } ], "tilewidth": 16 } ], "tilewidth": 16, "type": "map", "version": "1.10", "width": 40 } ================================================ FILE: src/App.tsx ================================================ import Game from './components/Game.tsx'; import { ToastContainer } from 'react-toastify'; import a16zImg from '../assets/a16z.png'; import convexImg from '../assets/convex.svg'; import starImg from '../assets/star.svg'; import helpImg from '../assets/help.svg'; // import { UserButton } from '@clerk/clerk-react'; // import { Authenticated, Unauthenticated } from 'convex/react'; // import LoginButton from './components/buttons/LoginButton.tsx'; import { useState } from 'react'; import ReactModal from 'react-modal'; import MusicButton from './components/buttons/MusicButton.tsx'; import Button from './components/buttons/Button.tsx'; import InteractButton from './components/buttons/InteractButton.tsx'; import FreezeButton from './components/FreezeButton.tsx'; import { MAX_HUMAN_PLAYERS } from '../convex/constants.ts'; import PoweredByConvex from './components/PoweredByConvex.tsx'; export default function Home() { const [helpModalOpen, setHelpModalOpen] = useState(false); return (
setHelpModalOpen(false)} style={modalStyles} contentLabel="Help modal" ariaHideApp={false} >

Help

Welcome to AI town. AI town supports both anonymous spectators and logged in{' '} interactivity.

Spectating

Click and drag to move around the town, and scroll in and out to zoom. You can click on an individual character to view its chat history.

Interactivity

If you log in, you can join the simulation and directly talk to different agents! After logging in, click the "Interact" button, and your character will appear somewhere on the map with a highlighted circle underneath you.

Controls:

Click to navigate around.

To talk to an agent, click on them and then click "Start conversation," which will ask them to start walking towards you. Once they're nearby, the conversation will start, and you can speak to each other. You can leave at any time by closing the conversation pane or moving away. They may propose a conversation to you - you'll see a button to accept in the messages panel.

AI town only supports {MAX_HUMAN_PLAYERS} humans at a time. If you're idle for five minutes, you'll be automatically removed from the simulation.

{/*
*/}

AI Town

A virtual town where AI characters live, chat and socialize. {/*
Log in to join the town
and the conversation! */}
a16z Convex
); } const modalStyles = { overlay: { backgroundColor: 'rgb(0, 0, 0, 75%)', zIndex: 12, }, content: { top: '50%', left: '50%', right: 'auto', bottom: 'auto', marginRight: '-50%', transform: 'translate(-50%, -50%)', maxWidth: '50%', border: '10px solid rgb(23, 20, 33)', borderRadius: '0', background: 'rgb(35, 38, 58)', color: 'white', fontFamily: '"Upheaval Pro", "sans-serif"', }, }; ================================================ FILE: src/components/Character.tsx ================================================ import { BaseTexture, ISpritesheetData, Spritesheet } from 'pixi.js'; import { useState, useEffect, useRef, useCallback } from 'react'; import { AnimatedSprite, Container, Graphics, Text } from '@pixi/react'; import * as PIXI from 'pixi.js'; export const Character = ({ textureUrl, spritesheetData, x, y, orientation, isMoving = false, isThinking = false, isSpeaking = false, emoji = '', isViewer = false, speed = 0.1, onClick, }: { // Path to the texture packed image. textureUrl: string; // The data for the spritesheet. spritesheetData: ISpritesheetData; // The pose of the NPC. x: number; y: number; orientation: number; isMoving?: boolean; // Shows a thought bubble if true. isThinking?: boolean; // Shows a speech bubble if true. isSpeaking?: boolean; emoji?: string; // Highlights the player. isViewer?: boolean; // The speed of the animation. Can be tuned depending on the side and speed of the NPC. speed?: number; onClick: () => void; }) => { const [spriteSheet, setSpriteSheet] = useState(); useEffect(() => { const parseSheet = async () => { const sheet = new Spritesheet( BaseTexture.from(textureUrl, { scaleMode: PIXI.SCALE_MODES.NEAREST, }), spritesheetData, ); await sheet.parse(); setSpriteSheet(sheet); }; void parseSheet(); }, []); // The first "left" is "right" but reflected. const roundedOrientation = Math.floor(orientation / 90); const direction = ['right', 'down', 'left', 'up'][roundedOrientation]; // Prevents the animation from stopping when the texture changes // (see https://github.com/pixijs/pixi-react/issues/359) const ref = useRef(null); useEffect(() => { if (isMoving) { ref.current?.play(); } }, [direction, isMoving]); if (!spriteSheet) return null; let blockOffset = { x: 0, y: 0 }; switch (roundedOrientation) { case 2: blockOffset = { x: -20, y: 0 }; break; case 0: blockOffset = { x: 20, y: 0 }; break; case 3: blockOffset = { x: 0, y: -20 }; break; case 1: blockOffset = { x: 0, y: 20 }; break; } return ( {isThinking && ( // TODO: We'll eventually have separate assets for thinking and speech animations. )} {isSpeaking && ( // TODO: We'll eventually have separate assets for thinking and speech animations. )} {isViewer && } {emoji && ( )} ); }; function ViewerIndicator() { const draw = useCallback((g: PIXI.Graphics) => { g.clear(); g.beginFill(0xffff0b, 0.5); g.drawRoundedRect(-10, 10, 20, 10, 100); g.endFill(); }, []); return ; } ================================================ FILE: src/components/ConvexClientProvider.tsx ================================================ import { ReactNode } from 'react'; import { ConvexReactClient, ConvexProvider } from 'convex/react'; // import { ConvexProviderWithClerk } from 'convex/react-clerk'; // import { ClerkProvider, useAuth } from '@clerk/clerk-react'; /** * Determines the Convex deployment to use. * * We perform load balancing on the frontend, by randomly selecting one of the available instances. * We use localStorage so that individual users stay on the same instance. */ function convexUrl(): string { const url = import.meta.env.VITE_CONVEX_URL as string; if (!url) { throw new Error('Couldn’t find the Convex deployment URL.'); } return url; } const convex = new ConvexReactClient(convexUrl(), { unsavedChangesWarning: false }); export default function ConvexClientProvider({ children }: { children: ReactNode }) { return ( // // {children} // // ); } ================================================ FILE: src/components/DebugPath.tsx ================================================ import { Graphics } from '@pixi/react'; import { Graphics as PixiGraphics } from 'pixi.js'; import { useCallback } from 'react'; import { Doc } from '../../convex/_generated/dataModel'; import { Player } from '../../convex/aiTown/player'; import { unpackPathComponent } from '../../convex/util/types'; export function DebugPath({ player, tileDim }: { player: Player; tileDim: number }) { const path = player.pathfinding?.state.kind == 'moving' && player.pathfinding.state.path; const draw = useCallback( (g: PixiGraphics) => { g.clear(); if (!path) { return; } let first = true; for (const p of path) { const { position } = unpackPathComponent(p as any); const x = position.x * tileDim + tileDim / 2; const y = position.y * tileDim + tileDim / 2; if (first) { g.moveTo(x, y); g.lineStyle(2, debugColor(player.id), 0.5); first = false; } else { g.lineTo(x, y); } } }, [path], ); return path ? : null; } function debugColor(_id: string) { return { h: 0, s: 50, l: 90 }; } ================================================ FILE: src/components/DebugTimeManager.tsx ================================================ import { HistoricalTimeManager } from '@/hooks/useHistoricalTime'; import { useEffect, useLayoutEffect, useRef, useState } from 'react'; import uPlot, { AlignedData, Options } from 'uplot'; const MAX_DATA_POINTS = 10000; export function DebugTimeManager(props: { timeManager: HistoricalTimeManager; width: number; height: number; }) { const [plotElement, setPlotElement] = useState(null); const [plot, setPlot] = useState(); useLayoutEffect(() => { if (!plotElement) { return; } const opts: Options = { width: props.width, height: props.height, series: [ {}, { stroke: 'white', spanGaps: true, pxAlign: 0, points: { show: false }, label: 'Buffer health', }, ], scales: { y: { distr: 1 }, }, axes: [ { side: 0, show: false, }, { ticks: { size: 0 }, side: 1, stroke: 'white', }, ], legend: { show: false, }, }; const data: AlignedData = [[], []]; const plot = new uPlot(opts, data, plotElement); setPlot(plot); }, [plotElement, props.width, props.height]); const timeManager = props.timeManager; const [intervals, setIntervals] = useState([...timeManager.intervals]); useEffect(() => { let reqId: ReturnType = 0; const data = { t: [] as number[], bufferHealth: [] as number[], }; const update = () => { if (plot) { if (data.t.length > MAX_DATA_POINTS) { data.t = data.t.slice(-MAX_DATA_POINTS); data.bufferHealth = data.bufferHealth.slice(-MAX_DATA_POINTS); } const now = Date.now() / 1000; data.t.push(now); data.bufferHealth.push(timeManager.bufferHealth()); setIntervals([...timeManager.intervals]); plot.setData([data.t, data.bufferHealth], true); plot.setScale('x', { min: now - 10, max: now }); } reqId = requestAnimationFrame(update); }; update(); return () => cancelAnimationFrame(reqId); }, [plot, timeManager]); let intervalNode: React.ReactNode | null = null; if (intervals.length > 0) { const base = intervals[0].startTs; const baseAge = Date.now() - base; intervalNode = (
{intervals.length} {intervals.length > 1 ? 'intervals' : 'interval'}:

Base: {toSeconds(baseAge)}s ago

{intervals.map((interval) => { const containsServerTs = timeManager.prevServerTs && interval.startTs < timeManager.prevServerTs && timeManager.prevServerTs <= interval.endTs; let serverTs = null; if (containsServerTs) { serverTs = ` (server: ${toSeconds((timeManager.prevServerTs ?? base) - base)})`; } return (
{toSeconds(interval.startTs - base)} - {toSeconds(interval.endTs - base)} {serverTs}
); })}
); } let statusNode: React.ReactNode | null = null; if (timeManager.latestEngineStatus) { const status = timeManager.latestEngineStatus; let statusMsg = status.running ? 'Running' : 'Stopped'; statusNode = (

Generation number: {status.generationNumber}

Input number: {status.processedInputNumber}

Status: {statusMsg}

Client skew: {toSeconds(timeManager.clockSkew())}s

); } timeManager.latestEngineStatus?.generationNumber; return (
Engine stats
{statusNode}
{intervalNode}
); } // D3's Tableau10 export const COLORS = ( '4e79a7f28e2ce1575976b7b259a14fedc949af7aa1ff9da79c755fbab0ab'.match(/.{6}/g) as string[] ).map((x) => `#${x}`); const toSeconds = (n: number) => (n / 1000).toFixed(2); ================================================ FILE: src/components/FreezeButton.tsx ================================================ import { useMutation, useQuery } from 'convex/react'; import { api } from '../../convex/_generated/api'; import Button from './buttons/Button'; export default function FreezeButton() { const stopAllowed = useQuery(api.testing.stopAllowed) ?? false; const defaultWorld = useQuery(api.world.defaultWorldStatus); const frozen = defaultWorld?.status === 'stoppedByDeveloper'; const unfreeze = useMutation(api.testing.resume); const freeze = useMutation(api.testing.stop); const flipSwitch = async () => { if (frozen) { console.log('Unfreezing'); await unfreeze(); } else { console.log('Freezing'); await freeze(); } }; return !stopAllowed ? null : ( <> ); } ================================================ FILE: src/components/Game.tsx ================================================ import { useRef, useState } from 'react'; import PixiGame from './PixiGame.tsx'; import { useElementSize } from 'usehooks-ts'; import { Stage } from '@pixi/react'; import { ConvexProvider, useConvex, useQuery } from 'convex/react'; import PlayerDetails from './PlayerDetails.tsx'; import { api } from '../../convex/_generated/api'; import { useWorldHeartbeat } from '../hooks/useWorldHeartbeat.ts'; import { useHistoricalTime } from '../hooks/useHistoricalTime.ts'; import { DebugTimeManager } from './DebugTimeManager.tsx'; import { GameId } from '../../convex/aiTown/ids.ts'; import { useServerGame } from '../hooks/serverGame.ts'; export const SHOW_DEBUG_UI = !!import.meta.env.VITE_SHOW_DEBUG_UI; export default function Game() { const convex = useConvex(); const [selectedElement, setSelectedElement] = useState<{ kind: 'player'; id: GameId<'players'>; }>(); const [gameWrapperRef, { width, height }] = useElementSize(); const worldStatus = useQuery(api.world.defaultWorldStatus); const worldId = worldStatus?.worldId; const engineId = worldStatus?.engineId; const game = useServerGame(worldId); // Send a periodic heartbeat to our world to keep it alive. useWorldHeartbeat(); const worldState = useQuery(api.world.worldState, worldId ? { worldId } : 'skip'); const { historicalTime, timeManager } = useHistoricalTime(worldState?.engine); const scrollViewRef = useRef(null); if (!worldId || !engineId || !game) { return null; } return ( <> {SHOW_DEBUG_UI && }
{/* Game area */}
{/* Re-propagate context because contexts are not shared between renderers. https://github.com/michalochman/react-pixi-fiber/issues/145#issuecomment-531549215 */}
{/* Right column area */}
); } ================================================ FILE: src/components/MessageInput.tsx ================================================ import clsx from 'clsx'; import { useMutation, useQuery } from 'convex/react'; import { KeyboardEvent, useRef, useState } from 'react'; import { api } from '../../convex/_generated/api'; import { Id } from '../../convex/_generated/dataModel'; import { useSendInput } from '../hooks/sendInput'; import { Player } from '../../convex/aiTown/player'; import { Conversation } from '../../convex/aiTown/conversation'; export function MessageInput({ worldId, engineId, humanPlayer, conversation, }: { worldId: Id<'worlds'>; engineId: Id<'engines'>; humanPlayer: Player; conversation: Conversation; }) { const descriptions = useQuery(api.world.gameDescriptions, { worldId }); const humanName = descriptions?.playerDescriptions.find((p) => p.playerId === humanPlayer.id) ?.name; const inputRef = useRef(null); const inflightUuid = useRef(); const writeMessage = useMutation(api.messages.writeMessage); const startTyping = useSendInput(engineId, 'startTyping'); const currentlyTyping = conversation.isTyping; const onKeyDown = async (e: KeyboardEvent) => { e.stopPropagation(); // Set the typing indicator if we're not submitting. if (e.key !== 'Enter') { console.log(inflightUuid.current); if (currentlyTyping || inflightUuid.current !== undefined) { return; } inflightUuid.current = crypto.randomUUID(); try { // Don't show a toast on error. await startTyping({ playerId: humanPlayer.id, conversationId: conversation.id, messageUuid: inflightUuid.current, }); } finally { inflightUuid.current = undefined; } return; } // Send the current message. e.preventDefault(); if (!inputRef.current) { return; } const text = inputRef.current.innerText; inputRef.current.innerText = ''; if (!text) { return; } let messageUuid = inflightUuid.current; if (currentlyTyping && currentlyTyping.playerId === humanPlayer.id) { messageUuid = currentlyTyping.messageUuid; } messageUuid = messageUuid || crypto.randomUUID(); await writeMessage({ worldId, playerId: humanPlayer.id, conversationId: conversation.id, text, messageUuid, }); }; return (
{humanName}

onKeyDown(e)} />

); } ================================================ FILE: src/components/Messages.tsx ================================================ import clsx from 'clsx'; import { Doc, Id } from '../../convex/_generated/dataModel'; import { useQuery } from 'convex/react'; import { api } from '../../convex/_generated/api'; import { MessageInput } from './MessageInput'; import { Player } from '../../convex/aiTown/player'; import { Conversation } from '../../convex/aiTown/conversation'; import { useEffect, useRef } from 'react'; export function Messages({ worldId, engineId, conversation, inConversationWithMe, humanPlayer, scrollViewRef, }: { worldId: Id<'worlds'>; engineId: Id<'engines'>; conversation: | { kind: 'active'; doc: Conversation } | { kind: 'archived'; doc: Doc<'archivedConversations'> }; inConversationWithMe: boolean; humanPlayer?: Player; scrollViewRef: React.RefObject; }) { const humanPlayerId = humanPlayer?.id; const descriptions = useQuery(api.world.gameDescriptions, { worldId }); const messages = useQuery(api.messages.listMessages, { worldId, conversationId: conversation.doc.id, }); let currentlyTyping = conversation.kind === 'active' ? conversation.doc.isTyping : undefined; if (messages !== undefined && currentlyTyping) { if (messages.find((m) => m.messageUuid === currentlyTyping!.messageUuid)) { currentlyTyping = undefined; } } const currentlyTypingName = currentlyTyping && descriptions?.playerDescriptions.find((p) => p.playerId === currentlyTyping?.playerId)?.name; const scrollView = scrollViewRef.current; const isScrolledToBottom = useRef(false); useEffect(() => { if (!scrollView) return undefined; const onScroll = () => { isScrolledToBottom.current = !!( scrollView && scrollView.scrollHeight - scrollView.scrollTop - 50 <= scrollView.clientHeight ); }; scrollView.addEventListener('scroll', onScroll); return () => scrollView.removeEventListener('scroll', onScroll); }, [scrollView]); useEffect(() => { if (isScrolledToBottom.current) { scrollViewRef.current?.scrollTo({ top: scrollViewRef.current.scrollHeight, behavior: 'smooth', }); } }, [messages, currentlyTyping]); if (messages === undefined) { return null; } if (messages.length === 0 && !inConversationWithMe) { return null; } const messageNodes: { time: number; node: React.ReactNode }[] = messages.map((m) => { const node = (
{m.authorName}

{m.text}

); return { node, time: m._creationTime }; }); const lastMessageTs = messages.map((m) => m._creationTime).reduce((a, b) => Math.max(a, b), 0); const membershipNodes: typeof messageNodes = []; if (conversation.kind === 'active') { for (const [playerId, m] of conversation.doc.participants) { const playerName = descriptions?.playerDescriptions.find((p) => p.playerId === playerId) ?.name; let started; if (m.status.kind === 'participating') { started = m.status.started; } if (started) { membershipNodes.push({ node: (

{playerName} joined the conversation.

), time: started, }); } } } else { for (const playerId of conversation.doc.participants) { const playerName = descriptions?.playerDescriptions.find((p) => p.playerId === playerId) ?.name; const started = conversation.doc.created; membershipNodes.push({ node: (

{playerName} joined the conversation.

), time: started, }); const ended = conversation.doc.ended; membershipNodes.push({ node: (

{playerName} left the conversation.

), // Always sort all "left" messages after the last message. // TODO: We can remove this once we want to support more than two participants per conversation. time: Math.max(lastMessageTs + 1, ended), }); } } const nodes = [...messageNodes, ...membershipNodes]; nodes.sort((a, b) => a.time - b.time); return (
{nodes.length > 0 && nodes.map((n) => n.node)} {currentlyTyping && currentlyTyping.playerId !== humanPlayerId && (
{currentlyTypingName}

typing...

)} {humanPlayer && inConversationWithMe && conversation.kind === 'active' && ( )}
); } ================================================ FILE: src/components/PixiGame.tsx ================================================ import * as PIXI from 'pixi.js'; import { useApp } from '@pixi/react'; import { Player, SelectElement } from './Player.tsx'; import { useEffect, useRef, useState } from 'react'; import { PixiStaticMap } from './PixiStaticMap.tsx'; import PixiViewport from './PixiViewport.tsx'; import { Viewport } from 'pixi-viewport'; import { Id } from '../../convex/_generated/dataModel'; import { useQuery } from 'convex/react'; import { api } from '../../convex/_generated/api.js'; import { useSendInput } from '../hooks/sendInput.ts'; import { toastOnError } from '../toasts.ts'; import { DebugPath } from './DebugPath.tsx'; import { PositionIndicator } from './PositionIndicator.tsx'; import { SHOW_DEBUG_UI } from './Game.tsx'; import { ServerGame } from '../hooks/serverGame.ts'; export const PixiGame = (props: { worldId: Id<'worlds'>; engineId: Id<'engines'>; game: ServerGame; historicalTime: number | undefined; width: number; height: number; setSelectedElement: SelectElement; }) => { // PIXI setup. const pixiApp = useApp(); const viewportRef = useRef(); const humanTokenIdentifier = useQuery(api.world.userStatus, { worldId: props.worldId }) ?? null; const humanPlayerId = [...props.game.world.players.values()].find( (p) => p.human === humanTokenIdentifier, )?.id; const moveTo = useSendInput(props.engineId, 'moveTo'); // Interaction for clicking on the world to navigate. const dragStart = useRef<{ screenX: number; screenY: number } | null>(null); const onMapPointerDown = (e: any) => { // https://pixijs.download/dev/docs/PIXI.FederatedPointerEvent.html dragStart.current = { screenX: e.screenX, screenY: e.screenY }; }; const [lastDestination, setLastDestination] = useState<{ x: number; y: number; t: number; } | null>(null); const onMapPointerUp = async (e: any) => { if (dragStart.current) { const { screenX, screenY } = dragStart.current; dragStart.current = null; const [dx, dy] = [screenX - e.screenX, screenY - e.screenY]; const dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { console.log(`Skipping navigation on drag event (${dist}px)`); return; } } if (!humanPlayerId) { return; } const viewport = viewportRef.current; if (!viewport) { return; } const gameSpacePx = viewport.toWorld(e.screenX, e.screenY); const tileDim = props.game.worldMap.tileDim; const gameSpaceTiles = { x: gameSpacePx.x / tileDim, y: gameSpacePx.y / tileDim, }; setLastDestination({ t: Date.now(), ...gameSpaceTiles }); const roundedTiles = { x: Math.floor(gameSpaceTiles.x), y: Math.floor(gameSpaceTiles.y), }; console.log(`Moving to ${JSON.stringify(roundedTiles)}`); await toastOnError(moveTo({ playerId: humanPlayerId, destination: roundedTiles })); }; const { width, height, tileDim } = props.game.worldMap; const players = [...props.game.world.players.values()]; // Zoom on the user’s avatar when it is created useEffect(() => { if (!viewportRef.current || humanPlayerId === undefined) return; const humanPlayer = props.game.world.players.get(humanPlayerId)!; viewportRef.current.animate({ position: new PIXI.Point(humanPlayer.position.x * tileDim, humanPlayer.position.y * tileDim), scale: 1.5, }); }, [humanPlayerId]); return ( {players.map( (p) => // Only show the path for the human player in non-debug mode. (SHOW_DEBUG_UI || p.id === humanPlayerId) && ( ), )} {lastDestination && } {players.map((p) => ( ))} ); }; export default PixiGame; ================================================ FILE: src/components/PixiStaticMap.tsx ================================================ import { PixiComponent, applyDefaultProps } from '@pixi/react'; import * as PIXI from 'pixi.js'; import { AnimatedSprite, WorldMap } from '../../convex/aiTown/worldMap'; import * as campfire from '../../data/animations/campfire.json'; import * as gentlesparkle from '../../data/animations/gentlesparkle.json'; import * as gentlewaterfall from '../../data/animations/gentlewaterfall.json'; import * as gentlesplash from '../../data/animations/gentlesplash.json'; import * as windmill from '../../data/animations/windmill.json'; const animations = { 'campfire.json': { spritesheet: campfire, url: '/ai-town/assets/spritesheets/campfire.png' }, 'gentlesparkle.json': { spritesheet: gentlesparkle, url: '/ai-town/assets/spritesheets/gentlesparkle32.png', }, 'gentlewaterfall.json': { spritesheet: gentlewaterfall, url: '/ai-town/assets/spritesheets/gentlewaterfall32.png', }, 'windmill.json': { spritesheet: windmill, url: '/ai-town/assets/spritesheets/windmill.png' }, 'gentlesplash.json': { spritesheet: gentlesplash, url: '/ai-town/assets/spritesheets/gentlewaterfall32.png',}, }; export const PixiStaticMap = PixiComponent('StaticMap', { create: (props: { map: WorldMap; [k: string]: any }) => { const map = props.map; const numxtiles = Math.floor(map.tileSetDimX / map.tileDim); const numytiles = Math.floor(map.tileSetDimY / map.tileDim); const bt = PIXI.BaseTexture.from(map.tileSetUrl, { scaleMode: PIXI.SCALE_MODES.NEAREST, }); const tiles = []; for (let x = 0; x < numxtiles; x++) { for (let y = 0; y < numytiles; y++) { tiles[x + y * numxtiles] = new PIXI.Texture( bt, new PIXI.Rectangle(x * map.tileDim, y * map.tileDim, map.tileDim, map.tileDim), ); } } const screenxtiles = map.bgTiles[0].length; const screenytiles = map.bgTiles[0][0].length; const container = new PIXI.Container(); const allLayers = [...map.bgTiles, ...map.objectTiles]; // blit bg & object layers of map onto canvas for (let i = 0; i < screenxtiles * screenytiles; i++) { const x = i % screenxtiles; const y = Math.floor(i / screenxtiles); const xPx = x * map.tileDim; const yPx = y * map.tileDim; // Add all layers of backgrounds. for (const layer of allLayers) { const tileIndex = layer[x][y]; // Some layers may not have tiles at this location. if (tileIndex === -1) continue; const ctile = new PIXI.Sprite(tiles[tileIndex]); ctile.x = xPx; ctile.y = yPx; container.addChild(ctile); } } // TODO: Add layers. const spritesBySheet = new Map(); for (const sprite of map.animatedSprites) { const sheet = sprite.sheet; if (!spritesBySheet.has(sheet)) { spritesBySheet.set(sheet, []); } spritesBySheet.get(sheet)!.push(sprite); } for (const [sheet, sprites] of spritesBySheet.entries()) { const animation = (animations as any)[sheet]; if (!animation) { console.error('Could not find animation', sheet); continue; } const { spritesheet, url } = animation; const texture = PIXI.BaseTexture.from(url, { scaleMode: PIXI.SCALE_MODES.NEAREST, }); const spriteSheet = new PIXI.Spritesheet(texture, spritesheet); spriteSheet.parse().then(() => { for (const sprite of sprites) { const pixiAnimation = spriteSheet.animations[sprite.animation]; if (!pixiAnimation) { console.error('Failed to load animation', sprite); continue; } const pixiSprite = new PIXI.AnimatedSprite(pixiAnimation); pixiSprite.animationSpeed = 0.1; pixiSprite.autoUpdate = true; pixiSprite.x = sprite.x; pixiSprite.y = sprite.y; pixiSprite.width = sprite.w; pixiSprite.height = sprite.h; container.addChild(pixiSprite); pixiSprite.play(); } }); } container.x = 0; container.y = 0; // Set the hit area manually to ensure `pointerdown` events are delivered to this container. container.interactive = true; container.hitArea = new PIXI.Rectangle( 0, 0, screenxtiles * map.tileDim, screenytiles * map.tileDim, ); return container; }, applyProps: (instance, oldProps, newProps) => { applyDefaultProps(instance, oldProps, newProps); }, }); ================================================ FILE: src/components/PixiViewport.tsx ================================================ // Based on https://codepen.io/inlet/pen/yLVmPWv. // Copyright (c) 2018 Patrick Brouwer, distributed under the MIT license. import { PixiComponent, useApp } from '@pixi/react'; import { Viewport } from 'pixi-viewport'; import { Application } from 'pixi.js'; import { MutableRefObject, ReactNode } from 'react'; export type ViewportProps = { app: Application; viewportRef?: MutableRefObject; screenWidth: number; screenHeight: number; worldWidth: number; worldHeight: number; children?: ReactNode; }; // https://davidfig.github.io/pixi-viewport/jsdoc/Viewport.html export default PixiComponent('Viewport', { create(props: ViewportProps) { const { app, children, viewportRef, ...viewportProps } = props; const viewport = new Viewport({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access events: app.renderer.events, passiveWheel: false, ...viewportProps, }); if (viewportRef) { viewportRef.current = viewport; } // Activate plugins viewport .drag() .pinch({}) .wheel() .decelerate() .clamp({ direction: 'all', underflow: 'center' }) .setZoom(-10) .clampZoom({ minScale: (1.04 * props.screenWidth) / (props.worldWidth / 2), maxScale: 3.0, }); return viewport; }, applyProps(viewport, oldProps: any, newProps: any) { Object.keys(newProps).forEach((p) => { if (p !== 'app' && p !== 'viewportRef' && p !== 'children' && oldProps[p] !== newProps[p]) { // @ts-expect-error Ignoring TypeScript here // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment viewport[p] = newProps[p]; } }); }, }); ================================================ FILE: src/components/Player.tsx ================================================ import { Character } from './Character.tsx'; import { orientationDegrees } from '../../convex/util/geometry.ts'; import { characters } from '../../data/characters.ts'; import { toast } from 'react-toastify'; import { Player as ServerPlayer } from '../../convex/aiTown/player.ts'; import { GameId } from '../../convex/aiTown/ids.ts'; import { Id } from '../../convex/_generated/dataModel'; import { Location, locationFields, playerLocation } from '../../convex/aiTown/location.ts'; import { useHistoricalValue } from '../hooks/useHistoricalValue.ts'; import { PlayerDescription } from '../../convex/aiTown/playerDescription.ts'; import { WorldMap } from '../../convex/aiTown/worldMap.ts'; import { ServerGame } from '../hooks/serverGame.ts'; export type SelectElement = (element?: { kind: 'player'; id: GameId<'players'> }) => void; const logged = new Set(); export const Player = ({ game, isViewer, player, onClick, historicalTime, }: { game: ServerGame; isViewer: boolean; player: ServerPlayer; onClick: SelectElement; historicalTime?: number; }) => { const playerCharacter = game.playerDescriptions.get(player.id)?.character; if (!playerCharacter) { throw new Error(`Player ${player.id} has no character`); } const character = characters.find((c) => c.name === playerCharacter); const locationBuffer = game.world.historicalLocations?.get(player.id); const historicalLocation = useHistoricalValue( locationFields, historicalTime, playerLocation(player), locationBuffer, ); if (!character) { if (!logged.has(playerCharacter)) { logged.add(playerCharacter); toast.error(`Unknown character ${playerCharacter}`); } return null; } if (!historicalLocation) { return null; } const isSpeaking = !![...game.world.conversations.values()].find( (c) => c.isTyping?.playerId === player.id, ); const isThinking = !isSpeaking && !![...game.world.agents.values()].find( (a) => a.playerId === player.id && !!a.inProgressOperation, ); const tileDim = game.worldMap.tileDim; const historicalFacing = { dx: historicalLocation.dx, dy: historicalLocation.dy }; return ( <> 0} isThinking={isThinking} isSpeaking={isSpeaking} emoji={ player.activity && player.activity.until > (historicalTime ?? Date.now()) ? player.activity?.emoji : undefined } isViewer={isViewer} textureUrl={character.textureUrl} spritesheetData={character.spritesheetData} speed={character.speed} onClick={() => { onClick({ kind: 'player', id: player.id }); }} /> ); }; ================================================ FILE: src/components/PlayerDetails.tsx ================================================ import { useQuery } from 'convex/react'; import { api } from '../../convex/_generated/api'; import { Id } from '../../convex/_generated/dataModel'; import closeImg from '../../assets/close.svg'; import { SelectElement } from './Player'; import { Messages } from './Messages'; import { toastOnError } from '../toasts'; import { useSendInput } from '../hooks/sendInput'; import { Player } from '../../convex/aiTown/player'; import { GameId } from '../../convex/aiTown/ids'; import { ServerGame } from '../hooks/serverGame'; export default function PlayerDetails({ worldId, engineId, game, playerId, setSelectedElement, scrollViewRef, }: { worldId: Id<'worlds'>; engineId: Id<'engines'>; game: ServerGame; playerId?: GameId<'players'>; setSelectedElement: SelectElement; scrollViewRef: React.RefObject; }) { const humanTokenIdentifier = useQuery(api.world.userStatus, { worldId }); const players = [...game.world.players.values()]; const humanPlayer = players.find((p) => p.human === humanTokenIdentifier); const humanConversation = humanPlayer ? game.world.playerConversation(humanPlayer) : undefined; // Always select the other player if we're in a conversation with them. if (humanPlayer && humanConversation) { const otherPlayerIds = [...humanConversation.participants.keys()].filter( (p) => p !== humanPlayer.id, ); playerId = otherPlayerIds[0]; } const player = playerId && game.world.players.get(playerId); const playerConversation = player && game.world.playerConversation(player); const previousConversation = useQuery( api.world.previousConversation, playerId ? { worldId, playerId } : 'skip', ); const playerDescription = playerId && game.playerDescriptions.get(playerId); const startConversation = useSendInput(engineId, 'startConversation'); const acceptInvite = useSendInput(engineId, 'acceptInvite'); const rejectInvite = useSendInput(engineId, 'rejectInvite'); const leaveConversation = useSendInput(engineId, 'leaveConversation'); if (!playerId) { return (
Click on an agent on the map to see chat history.
); } if (!player) { return null; } const isMe = humanPlayer && player.id === humanPlayer.id; const canInvite = !isMe && !playerConversation && humanPlayer && !humanConversation; const sameConversation = !isMe && humanPlayer && humanConversation && playerConversation && humanConversation.id === playerConversation.id; const humanStatus = humanPlayer && humanConversation && humanConversation.participants.get(humanPlayer.id)?.status; const playerStatus = playerConversation && playerConversation.participants.get(playerId)?.status; const haveInvite = sameConversation && humanStatus?.kind === 'invited'; const waitingForAccept = sameConversation && playerConversation.participants.get(playerId)?.status.kind === 'invited'; const waitingForNearby = sameConversation && playerStatus?.kind === 'walkingOver' && humanStatus?.kind === 'walkingOver'; const inConversationWithMe = sameConversation && playerStatus?.kind === 'participating' && humanStatus?.kind === 'participating'; const onStartConversation = async () => { if (!humanPlayer || !playerId) { return; } console.log(`Starting conversation`); await toastOnError(startConversation({ playerId: humanPlayer.id, invitee: playerId })); }; const onAcceptInvite = async () => { if (!humanPlayer || !humanConversation || !playerId) { return; } await toastOnError( acceptInvite({ playerId: humanPlayer.id, conversationId: humanConversation.id, }), ); }; const onRejectInvite = async () => { if (!humanPlayer || !humanConversation) { return; } await toastOnError( rejectInvite({ playerId: humanPlayer.id, conversationId: humanConversation.id, }), ); }; const onLeaveConversation = async () => { if (!humanPlayer || !inConversationWithMe || !humanConversation) { return; } await toastOnError( leaveConversation({ playerId: humanPlayer.id, conversationId: humanConversation.id, }), ); }; // const pendingSuffix = (inputName: string) => // [...inflightInputs.values()].find((i) => i.name === inputName) ? ' opacity-50' : ''; const pendingSuffix = (s: string) => ''; return ( <>

{playerDescription?.name}

setSelectedElement(undefined)} >

{canInvite && (
Start conversation
)} {waitingForAccept && (
Waiting for accept...
)} {waitingForNearby && (
Walking over...
)} {inConversationWithMe && (
Leave conversation
)} {haveInvite && ( <>
Accept
Reject
)} {!playerConversation && player.activity && player.activity.until > Date.now() && (

{player.activity.description}

)}

{!isMe && playerDescription?.description} {isMe && This is you!} {!isMe && inConversationWithMe && ( <>

(Conversing with you!) )}

{!isMe && playerConversation && playerStatus?.kind === 'participating' && ( )} {!playerConversation && previousConversation && ( <>

Previous conversation

)} ); } ================================================ FILE: src/components/PositionIndicator.tsx ================================================ import { useCallback, useState } from 'react'; import { Graphics } from '@pixi/react'; import { Graphics as PixiGraphics } from 'pixi.js'; const ANIMATION_DURATION = 500; const RADIUS_TILES = 0.25; export function PositionIndicator(props: { destination: { x: number; y: number; t: number }; tileDim: number; }) { const { destination, tileDim } = props; const draw = (g: PixiGraphics) => { g.clear(); const now = Date.now(); if (destination.t + ANIMATION_DURATION <= now) { return; } const progress = (now - destination.t) / ANIMATION_DURATION; const x = destination.x * tileDim; const y = destination.y * tileDim; g.lineStyle(1.5, { h: 0, s: 50, l: 90 }, 0.5); g.drawCircle(x, y, RADIUS_TILES * progress * tileDim); }; return ; } ================================================ FILE: src/components/PoweredByConvex.tsx ================================================ import bannerBg from '../../assets/convex-bg.webp'; export default function PoweredByConvex() { return (
Powered by Convex
); } ================================================ FILE: src/components/buttons/Button.tsx ================================================ import clsx from 'clsx'; import { MouseEventHandler, ReactNode } from 'react'; export default function Button(props: { className?: string; href?: string; imgUrl: string; onClick?: MouseEventHandler; title?: string; children: ReactNode; }) { return (
{props.children}
); } ================================================ FILE: src/components/buttons/InteractButton.tsx ================================================ import Button from './Button'; import { toast } from 'react-toastify'; import interactImg from '../../../assets/interact.svg'; import { useConvex, useMutation, useQuery } from 'convex/react'; import { api } from '../../../convex/_generated/api'; // import { SignInButton } from '@clerk/clerk-react'; import { ConvexError } from 'convex/values'; import { Id } from '../../../convex/_generated/dataModel'; import { useCallback } from 'react'; import { waitForInput } from '../../hooks/sendInput'; import { useServerGame } from '../../hooks/serverGame'; export default function InteractButton() { // const { isAuthenticated } = useConvexAuth(); const worldStatus = useQuery(api.world.defaultWorldStatus); const worldId = worldStatus?.worldId; const game = useServerGame(worldId); const humanTokenIdentifier = useQuery(api.world.userStatus, worldId ? { worldId } : 'skip'); const userPlayerId = game && [...game.world.players.values()].find((p) => p.human === humanTokenIdentifier)?.id; const join = useMutation(api.world.joinWorld); const leave = useMutation(api.world.leaveWorld); const isPlaying = !!userPlayerId; const convex = useConvex(); const joinInput = useCallback( async (worldId: Id<'worlds'>) => { let inputId; try { inputId = await join({ worldId }); } catch (e: any) { if (e instanceof ConvexError) { toast.error(e.data); return; } throw e; } try { await waitForInput(convex, inputId); } catch (e: any) { toast.error(e.message); } }, [convex], ); const joinOrLeaveGame = () => { if ( !worldId || // || !isAuthenticated game === undefined ) { return; } if (isPlaying) { console.log(`Leaving game for player ${userPlayerId}`); void leave({ worldId }); } else { console.log(`Joining game`); void joinInput(worldId); } }; // if (!isAuthenticated || game === undefined) { // return ( // // // // ); // } return ( ); } ================================================ FILE: src/components/buttons/LoginButton.tsx ================================================ import { SignInButton } from '@clerk/clerk-react'; export default function LoginButton() { return ( ); } ================================================ FILE: src/components/buttons/MusicButton.tsx ================================================ import { useCallback, useEffect, useState } from 'react'; import volumeImg from '../../../assets/volume.svg'; import { sound } from '@pixi/sound'; import Button from './Button'; import { useQuery } from 'convex/react'; import { api } from '../../../convex/_generated/api'; export default function MusicButton() { const musicUrl = useQuery(api.music.getBackgroundMusic); const [isPlaying, setPlaying] = useState(false); useEffect(() => { if (musicUrl) { sound.add('background', musicUrl).loop = true; } }, [musicUrl]); const flipSwitch = async () => { if (isPlaying) { sound.stop('background'); } else { await sound.play('background'); } setPlaying(!isPlaying); }; const handleKeyPress = useCallback( (event: { key: string }) => { if (event.key === 'm' || event.key === 'M') { void flipSwitch(); } }, [flipSwitch], ); useEffect(() => { window.addEventListener('keydown', handleKeyPress); return () => window.removeEventListener('keydown', handleKeyPress); }, [handleKeyPress]); return ( <> ); } ================================================ FILE: src/editor/README.md ================================================ # Level Editor ## Setup 1. Run `npm run le` to start the level editor (localhost:5174) 2. Import a map composite file (.js) into the level config input under the config tab 3. Customize your tileset by either: - Inputting it in the config tab - Modifying the `DEFAULTTILESETPATH` in leconfig.js (e.g. replace `export const DEFAULTTILESETPATH = "./tilesets/gentle.png"` with your tileset path) ## Map Editing Instructions ### Basic Controls - Select tiles from the tileset in the top right pane (like selecting a paint brush) - Click in the top left pane to place tiles on the background level (no collision) - Click in the second row left pane to place tiles on the object level (with collision) ### Keyboard Shortcuts - `f` - Fill level 0 with current tile - `Ctrl+z` - Undo - `g` - Overlay 32x32 grid - `s` - Generate .js file to move over to convex/maps/ - `m` - Place semi-transparent red mask over tiles (helps find invisible tiles) - `d` - Hold while clicking a tile to delete - `p` - Toggle between 16pixel and 32 pixel ## Map Composite File Structure After saving your map, you will receive a JavaScript export object similar to the following: ```js export const tilesetpath = "./tilesets/gentle-obj.png"; export const tiledim = 32; export const screenxtiles = 45; export const screenytiles = 32; export const tilesetpxw = 1440; export const tilesetpxh = 1024; export const bgtiles = [ // 2D array representing the background (no collision) ]; export const objmap = [ // 2D array representing the object layer (with collision) ]; ``` Explanation of each property - `tilesetpath`: Path to the tileset image that will be used as the “paint brushes” for your map. - `tiledim`: The pixel dimension of each tile (e.g., 32px). - `screenxtiles / screenytiles`: The map size in terms of tile columns and rows (e.g., 45 tiles wide × 32 tiles tall). - `tilesetpxw / tilesetpxh`: The total pixel width and height of your tileset image (not the map). - `bgtiles`: A 2D array storing the background layer (no collision). - `objmap`: A 2D array storing the object layer (with collision). Each entry is a number referencing the tile coordinate in your tileset. To 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 ```bash just convex run testing:wipeAllTables ``` and then run to restart the world ```bash just convex run init ``` ================================================ FILE: src/editor/campfire.json ================================================ {"frames": { "pixels_large1.png": { "frame": {"x":0,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} }, "pixels_large2.png": { "frame": {"x":32,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} }, "pixels_large3.png": { "frame": {"x":64,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} }, "pixels_large4.png": { "frame": {"x":96,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} } }, "animations": { "pixels_large": ["pixels_large1.png","pixels_large2.png","pixels_large3.png","pixels_large4.png"] }, "meta": { "image": "./spritesheets/campfire.png", "format": "RGBA8888", "size": {"w":128,"h":32}, "scale": "1" } } ================================================ FILE: src/editor/eutils.js ================================================ // Function to download data to a file export function download(data, filename, type) { var file = new Blob([data], {type: type}); if (window.navigator.msSaveOrOpenBlob) // IE10+ window.navigator.msSaveOrOpenBlob(file, filename); else { // Others var a = document.createElement("a"), url = URL.createObjectURL(file); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); setTimeout(function() { document.body.removeChild(a); window.URL.revokeObjectURL(url); }, 0); } } ================================================ FILE: src/editor/gentlesparkle.json ================================================ {"frames": { "pixels_large1.png": { "frame": {"x":0,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} }, "pixels_large2.png": { "frame": {"x":32,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} }, "pixels_large3.png": { "frame": {"x":64,"y":0,"w":32,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, "sourceSize": {"w":32,"h":32} } }, "animations": { "pixels_large": ["pixels_large1.png","pixels_large2.png","pixels_large3.png"] }, "meta": { "image": "./spritesheets/gentlesparkle32.png", "format": "RGBA8888", "size": {"w":192,"h":320}, "scale": "1" } } ================================================ FILE: src/editor/gentlesplash.json ================================================ {"frames": { "pixels_large1.png": { "frame": {"x":0,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} }, "pixels_large2.png": { "frame": {"x":32,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} }, "pixels_large3.png": { "frame": {"x":64,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} }, "pixels_large4.png": { "frame": {"x":64,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} }, "pixels_large5.png": { "frame": {"x":128,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} }, "pixels_large6.png": { "frame": {"x":160,"y":192,"w":32,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, "sourceSize": {"w":32,"h":64} } }, "animations": { "pixels_large": ["pixels_large1.png","pixels_large2.png","pixels_large3.png","pixels_large4.png","pixels_large5.png","pixels_large6.png"] }, "meta": { "image": "./spritesheets/gentlewaterfall32.png", "format": "RGBA8888", "size": {"w":192,"h":320}, "scale": "1" } } ================================================ FILE: src/editor/gentlewaterfall.json ================================================ {"frames": { "pixels_large1.png": { "frame": {"x":0,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} }, "pixels_large2.png": { "frame": {"x":32,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} }, "pixels_large3.png": { "frame": {"x":64,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} }, "pixels_large4.png": { "frame": {"x":96,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} }, "pixels_large5.png": { "frame": {"x":128,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} }, "pixels_large6.png": { "frame": {"x":160,"y":32,"w":32,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":96}, "sourceSize": {"w":32,"h":96} } }, "animations": { "pixels_large": ["pixels_large1.png","pixels_large2.png","pixels_large3.png","pixels_large4.png","pixels_large5.png","pixels_large6.png"] }, "meta": { "image": "./spritesheets/gentlewaterfall32.png", "format": "RGBA8888", "size": {"w":192,"h":320}, "scale": "1" } } ================================================ FILE: src/editor/index.html ================================================ Level editer
Sprite editer ================================================ FILE: src/editor/le.html ================================================
Composite to png Load level Load png to Composite Load Sprite
16 32
Load Tileset
================================================ FILE: src/editor/le.js ================================================ // -- // Simple level editer. // // TODO: // -- right now if plaxing a sprite, will place based on selected tiles. So need to clear that when // loading a sprite // -- fix hardcoded animations, hack of putting spritesheet into g_ctx etc // -- create tab that contains all animations for a given json file // -- add portals to level for character start positions // -- if you load an animated sprite and then load a level, it just puts the sprite everywhere // // // Done: // -- fix level load bug where texture doesn't fit (load, mage, serene and then gentle) // -- write maps with sprites // - clear selected_tiles // - Delete tiles // - move magic numbers to context / initialization (zIndex, pane size etc.) // - todo fudge factor on g_ctx.tileset // - get rid of dangerous CONFIG.tiledim (use g_ctx.tileDim instead) // - XXX create tilesetpadding for tilesets whos tiles are spaced (e.g. phantasy star II) // - only use fudge to pick sprites rather than fudge and non // - use g_ctx for g_ctx.tileset parameters instead of CONFIG (starting with initTilesetConfig) // - todo print locations on screen // // // Keybindings: // f - fill level 0 with current tile // -z - undo // g - overlay 32x32 grid // s - generate .js file to move over to convex/maps/ // m - place a semi-transparent red mask over all tiles. This helps find invisible tiles // d - hold while clicking a tile to delete // p - toggle between 16pixel and 32 pixel. // // Known bugs and annoyances // - if deleting a tile while filter is on, filter isn't refreshed so need to toggle with "m" // -- import * as PIXI from 'pixi.js' import { g_ctx } from './lecontext.js' // global context import * as CONFIG from './leconfig.js' import * as UNDO from './undo.js' import * as MAPFILE from './mapfile.js' import * as UI from './lehtmlui.js' import { EventSystem } from '@pixi/events'; g_ctx.debug_flag = true; g_ctx.debug_flag2 = false; // really verbose output function tileset_index_from_coords(x, y) { let retme = x + (y*g_ctx.tilesettilew); console.log("tileset_index_from_coord ",retme, x, y); return retme; } function level_index_from_coords(x, y) { // place 16px tiles in separate index space let offset = (g_ctx.tiledimx == 16)? CONFIG.MAXTILEINDEX : 0; let retme = x + (y*CONFIG.leveltilewidth) + offset; return retme; } function tileset_index_from_px(x, y) { let coord_x = Math.floor(x / (g_ctx.tiledimx + CONFIG.tilesetpadding)); let coord_y = Math.floor(y / (g_ctx.tiledimx+ CONFIG.tilesetpadding)); console.log("tileset_index_from_px ",x, y); return tileset_index_from_coords(coord_x, coord_y); } function level_index_from_px(x, y) { let coord_x = Math.floor(x / g_ctx.tiledimx); let coord_y = Math.floor(y / g_ctx.tiledimx); return level_index_from_coords(coord_x, coord_y); } function tileset_coords_from_index(index) { let x = index % (g_ctx.tilesettilew); let y = Math.floor(index / (g_ctx.tilesettilew)); // console.log("tilesettilewidth: ",g_ctx.tilesettilew); // console.log("tileset_coords_from_index tile coords: ",index,x,y); return [x,y]; } function tileset_px_from_index(index) { let ret = tileset_coords_from_index(index); return [ret[0] * (g_ctx.tiledimx+CONFIG.tilesetpadding), ret[1] * (g_ctx.tiledimx+CONFIG.tilesetpadding)] ; } // return a sprite of size tileDim given (x,y) starting location function sprite_from_px(x, y) { const bt = PIXI.BaseTexture.from(g_ctx.tilesetpath, { scaleMode: PIXI.SCALE_MODES.NEAREST, }); let texture = new PIXI.Texture(bt, new PIXI.Rectangle(x, y, g_ctx.tiledimx, g_ctx.tiledimx), ); return new PIXI.Sprite(texture); } function DragState() { this.square = new PIXI.Graphics(); this.tooltip = new PIXI.Text('', { fontFamily: 'Courier', fontSize: 12, fill: 0xffffff, align: 'center', }); this.startx = 0; this.starty = 0; this.endx = 0; this.endy = 0; } class LayerContext { constructor(app, pane, num, mod = null) { this.app = app; this.scrollpane = pane; this.num = num; this.widthpx = CONFIG.levelwidth; this.heightpx = CONFIG.levelheight; this.container = new PIXI.Container(); this.sprites = {}; this.composite_sprites = {}; this.dragctx = new DragState(); app.stage.addChild(this.container); this.mouseshadow = new PIXI.Container(); this.mouseshadow.zIndex = CONFIG.zIndexMouseShadow; this.lasttileindex = -1; // current tileset index this.curanimatedtile = null; this.fudgex = 0; // offset from 0,0 this.fudgey = 0; this.square = new PIXI.Graphics(); this.square.beginFill(0x2980b9); this.square.drawRect(0, 0, CONFIG.levelwidth, CONFIG.levelheight); this.square.endFill(); this.square.eventMode = 'static'; this.container.addChild(this.square); this.square.on('mousemove', onLevelMousemove.bind(this)); this.square.on('mouseover', onLevelMouseover.bind(this)); this.square.on('pointerout', onLevelMouseOut.bind(this)) this.square.on('pointerdown', onLevelPointerDown.bind(null, this)) .on('pointerup', onLevelDragEnd.bind(null, this)) .on('pointerupoutside', onLevelDragEnd.bind(null, this)); if (mod != null && !(mod === g_ctx)) { this.loadFromMapFile(mod); } } loadFromMapFile(mod) { let tiles = []; if (this.num == 0) { tiles = mod.bgtiles[0]; } else if (this.num == 1) { tiles = mod.bgtiles[1]; } else if (this.num == 2) { tiles = mod.objmap[0]; } else if (this.num == 3) { tiles = mod.objmap[1]; } else { console.log("loadFromMapFile: Error unknow layer number"); return; } for (let x = 0; x < tiles.length; x++) { for (let y = 0; y < tiles[0].length; y++) { if (tiles[x][y] != -1) { this.addTileLevelCoords(x, y, mod.tiledim, tiles[x][y]); } } } } // this will create a rectangle with an alpha channel for every square that has a sprite. This helps find // sprites that are purely transparent drawFilter() { if (typeof this.filtergraphics == 'undefined') { this.filtertoggle = true; this.filtergraphics = new PIXI.Graphics(); this.filtergraphics.zIndex = CONFIG.zIndexFilter; } if (this.filtertoggle) { this.filtergraphics.beginFill(0xff0000, 0.3); for (let i in this.sprites) { let spr = this.sprites[i]; this.filtergraphics.drawRect(spr.x, spr.y, g_ctx.tiledimx, g_ctx.tiledimx); } this.filtergraphics.endFill(); this.container.addChild(this.filtergraphics); }else{ this.filtergraphics.clear(); this.container.removeChild(this.filtergraphics); } this.filtertoggle = ! this.filtertoggle; } // add tile of "index" to Level at location x,y addTileLevelCoords(x, y, dim, index) { return this.addTileLevelPx(x * dim, y * dim, index); } // add tile of tileset "index" to Level at location x,y addTileLevelPx(x, y, index) { if (x > CONFIG.levelwidth || y > CONFIG.levelheight){ console.log("tile placed outside of level boundary, ignoring",x,y) return -1; } let xPx = x; let yPx = y; let ctile = null; let ctile2 = null; if(g_ctx.spritesheet != null){ ctile = new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']); ctile2 = new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']); ctile.animationSpeed = .1; ctile2.animationSpeed = .1; ctile.autoUpdate = true; ctile2.autoUpdate = true; ctile.play(); ctile2.play(); // HACK for now just stuff animated sprite details into the sprite ctile.animationname = 'row0'; ctile.spritesheetname = g_ctx.spritesheetname; } else { let pxloc = tileset_px_from_index(index); ctile = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); ctile.index = index; ctile2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); } // snap to grid const dx = g_ctx.tiledimx; const dy = g_ctx.tiledimy; ctile.x = Math.floor(xPx / dx) * dx; ctile2.x = Math.floor(xPx / dx) * dx; ctile.y = Math.floor(yPx / dy) * dy; ctile2.y = Math.floor(yPx / dy) * dy; ctile2.zIndex = this.num; // console.log(xPx,yPx,ctile.x,ctile.y); let new_index = level_index_from_px(ctile.x, ctile.y); if(g_ctx.debug_flag2){ console.log('addTileLevelPx ',this.num,' ctile.x ', ctile.x, 'ctile.y ', ctile.y, "index ", index, "new_index", new_index); } if (!g_ctx.dkey) { this.container.addChild(ctile); g_ctx.composite.container.addChild(ctile2); } if (this.sprites.hasOwnProperty(new_index)) { if(g_ctx.debug_flag){ console.log("addTileLevelPx: ",this.num,"removing old tile", new_index); } this.container.removeChild(this.sprites[new_index]); delete this.sprites[new_index]; g_ctx.composite.container.removeChild(this.composite_sprites[new_index]); delete this.composite_sprites[new_index]; } if (!g_ctx.dkey) { this.sprites[new_index] = ctile; this.composite_sprites[new_index] = ctile2; } else if (typeof this.filtergraphics != 'undefined') { this.filtergraphics.clear(); this.drawFilter(); this.drawFilter(); } // consolelog("SETTING ZINDEX ", this.composite_sprites[new_index].zIndex); return new_index; } } // class LayerContext class TilesetContext { constructor(app, mod = g_ctx) { this.app = app; this.container = new PIXI.Container(); this.widthpx = g_ctx.tilesetpxw; this.heightpx = g_ctx.tilesetpxh; console.log(mod.tilesetpath); const texture = PIXI.Texture.from(mod.tilesetpath); const bg = new PIXI.Sprite(texture); this.square = new PIXI.Graphics(); this.square.beginFill(0x2980b9); this.square.drawRect(0, 0, mod.tilesetpxw, mod.tilesetpxh); this.square.endFill(); this.square.eventMode = 'static'; this.container.addChild(this.square); this.container.addChild(bg); this.app.stage.addChild(this.container); this.fudgex = 0; // offset from 0,0 this.fudgey = 0; this.dragctx = new DragState(); this.square.on('mousedown', function (e) { // if a spritesheet has been loaded from a file, delete // FIXME, we should be able to add animated tiles to the // tileset ... if(g_ctx.spritesheet != null){ // FIXME .. creating a leak here. But animatedsprites are still on the map so // cannot destroy. In the future these should be part of the UI // g_ctx.spritesheet.destroy(); g_ctx.spritesheet = null; } g_ctx.tile_index = tileset_index_from_px(e.global.x, e.global.y); if(g_ctx.debug_flag) { console.log("g_ctx.tileset mouse down. index "+g_ctx.tile_index); } }); this.square.on('pointerdown', onTilesetDragStart) .on('pointerup', onTilesetDragEnd) .on('pointerupoutside', onTilesetDragEnd); } addTileSheet(name, sheet){ console.log(" tileset.addTileSheet ", sheet); // FIXME ... development code g_ctx.spritesheet = sheet; g_ctx.spritesheetname = name; let as = new PIXI.AnimatedSprite(sheet.animations['row0']); as.animationSpeed = .1; as.autoUpdate = true; as.play(); as.alpha = .5; g_ctx.g_layers[0].curanimatedtile = as; } } // class TilesetContext class CompositeContext { constructor(app) { this.app = app; this.widthpx = CONFIG.levelwidth; this.heightpx = CONFIG.levelheight; this.container = new PIXI.Container(); this.container.sortableChildren = true; this.app.stage.addChild(this.container); this.sprites = {}; this.circle = new PIXI.Graphics(); this.circle.zIndex = CONFIG.zIndexCompositePointer; this.fudgex = 0; // offset from 0,0 this.fudgey = 0; this.mouseshadow = new PIXI.Container(); this.mouseshadow.zIndex = CONFIG.zIndexMouseShadow; this.lasttileindex = -1; this.square = new PIXI.Graphics(); this.square.beginFill(0x2980b9); this.square.drawRect(0, 0, CONFIG.levelwidth, CONFIG.levelheight); this.square.endFill(); this.square.eventMode = 'static'; this.container.addChild(this.square); this.square.on('mousedown', onCompositeMousedown.bind(null, this)); } } // class CompositeContext function loadAnimatedSpritesFromModule(mod){ if(!('animatedsprites' in mod) || mod.animatedsprites.length <= 0){ return; } let m = new Map(); for(let x = 0; x < mod.animatedsprites.length; x++){ let spr = mod.animatedsprites[x]; if(! m.has(spr.sheet)){ m.set(spr.sheet, [spr]); }else{ m.get(spr.sheet).push(spr); } } for(let key of m.keys()){ console.log("loadAnimatedSpritesFromModule: ",key); PIXI.Assets.load("./"+key).then( function(sheet) { // setup global state so we can use layer addTileLevelMethod g_ctx.spritesheet = sheet; g_ctx.spritesheetname = key; let asprarray = m.get(key); for (let asprite of asprarray) { // TODO FIXME, pass in animation name console.log("Loading animation", asprite.animation); g_ctx.g_layers[asprite.layer].addTileLevelPx(asprite.x, asprite.y, -1); } g_ctx.spritesheet = null; g_ctx.spritesheetname = null; } ); } } function loadMapFromModuleFinish(mod) { g_ctx.composite.container.removeChildren(); g_ctx.tileset_app.stage.removeChildren() g_ctx.tileset = new TilesetContext(g_ctx.tileset_app, mod); g_ctx.g_layer_apps[0].stage.removeChildren() g_ctx.g_layers[0] = new LayerContext(g_ctx.g_layer_apps[0], document.getElementById("layer0pane"), 0, mod); g_ctx.g_layer_apps[1].stage.removeChildren() g_ctx.g_layers[1] = new LayerContext(g_ctx.g_layer_apps[1], document.getElementById("layer1pane"), 1, mod); g_ctx.g_layer_apps[2].stage.removeChildren() g_ctx.g_layers[2] = new LayerContext(g_ctx.g_layer_apps[2], document.getElementById("layer2pane"), 2, mod); g_ctx.g_layer_apps[3].stage.removeChildren() g_ctx.g_layers[3] = new LayerContext(g_ctx.g_layer_apps[3], document.getElementById("layer3pane"), 3, mod); loadAnimatedSpritesFromModule(mod); } function loadMapFromModule(mod) { g_ctx.tilesetpath = mod.tilesetpath; initTilesSync(loadMapFromModuleFinish.bind(null, mod)); initTiles(); } function downloadpng(filename) { let newcontainer = new PIXI.Container(); let children = [...g_ctx.composite.container.children]; for(let i = 0; i < children.length; i++) { let child = children[i]; if (! child.hasOwnProperty('isSprite') || !child.isSprite){ console.log(child); continue; } // console.log(child, typeof child); g_ctx.composite.container.removeChild(child); newcontainer.addChild(child); } const { renderer } = g_ctx.composite_app; renderer.plugins.extract.canvas(newcontainer).toBlob(function (b) { console.log(b); var a = document.createElement("a"); document.body.append(a); a.download = filename; a.href = URL.createObjectURL(b); a.click(); a.remove(); }, "image/png"); } window.saveCompositeAsImage = () => { downloadpng("g_ctx.composite.png"); } window.onTab = (evt, tabName) => { // Declare all variables var i, tabcontent, tablinks; // Get all elements with class="tabcontent" and hide them tabcontent = document.getElementsByClassName("tabcontent"); for (i = 0; i < tabcontent.length; i++) { tabcontent[i].style.display = "none"; } // Get all elements with class="tablinks" and remove the class "active" tablinks = document.getElementsByClassName("tablinks"); for (i = 0; i < tablinks.length; i++) { tablinks[i].className = tablinks[i].className.replace(" active", ""); } // Show the current tab, and add an "active" class to the button that opened the tab document.getElementById(tabName).style.display = "block"; evt.currentTarget.className += " active"; if (tabName == "map"){ g_ctx.map_app.stage.addChild(g_ctx.composite.container); }else { g_ctx.composite.app.stage.addChild(g_ctx.composite.container); } } // fill base level with currentIndex tile window.fill0 = () => { UNDO.undo_mark_task_start(g_ctx.g_layers[0]); for(let i = 0; i < CONFIG.levelwidth / g_ctx.tiledimx; i++){ for(let j = 0; j < CONFIG.levelheight / g_ctx.tiledimx; j++){ let ti = g_ctx.g_layers[0].addTileLevelCoords(i,j,g_ctx.tiledimx, g_ctx.tile_index); UNDO.undo_add_index_to_task(ti); } } UNDO.undo_mark_task_end(); } window.addEventListener( "keyup", (event) => { if (event.code == "KeyD"){ g_ctx.dkey = false; g_ctx.g_layers.map( (l) => l.container.addChild(l.mouseshadow)); g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow); } }); window.addEventListener( "keydown", (event) => { if (event.code == "KeyD"){ g_ctx.dkey = true; g_ctx.g_layers.map((l) => l.container.removeChild(l.mouseshadow) ); g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow); } if (event.code == 'KeyF'){ window.fill0(); } else if (event.code == 'KeyS'){ MAPFILE.generate_level_file(); } else if (event.code == 'Escape'){ g_ctx.selected_tiles = []; g_ctx.g_layers.map((l) => l.mouseshadow.removeChildren()); g_ctx.composite.mouseshadow.removeChildren(); } else if (event.code == 'KeyM'){ g_ctx.g_layers.map((l) => l.drawFilter () ); }else if (event.code == 'KeyP'){ setGridSize((g_ctx.tiledimx == 16)?32:16); } else if (event.code == 'KeyG'){ g_ctx.g_layers.map((l) => redrawGrid (l, false) ); redrawGrid(g_ctx.tileset, false); redrawGrid(g_ctx.composite, false); } else if (event.ctrlKey && event.code === 'KeyZ'){ let undome = UNDO.undo_pop(); if (!undome) { return; } let layer = undome.shift(); for(let i = 0; i < undome.length; i++) { if (g_ctx.debug_flag) { console.log("Undo removing ", undome[i]) } // Remove current tile layer.container.removeChild(layer.sprites[undome[i][0]]); g_ctx.composite.container.removeChild(layer.composite_sprites[undome[i][0]]); // Restore original tile if it existed if (undome[i][1] !== -1) { let pxloc = tileset_px_from_index(undome[i][1]); let originalTile = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); let originalTile2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); // Position tiles at the correct location let x = Math.floor(undome[i][0] % CONFIG.leveltilewidth) * g_ctx.tiledimx; let y = Math.floor(undome[i][0] / CONFIG.leveltilewidth) * g_ctx.tiledimx; originalTile.x = x; originalTile.y = y; originalTile2.x = x; originalTile2.y = y; originalTile2.zIndex = layer.num; // Add tiles back to containers layer.container.addChild(originalTile); g_ctx.composite.container.addChild(originalTile2); // Update sprite references layer.sprites[undome[i][0]] = originalTile; layer.composite_sprites[undome[i][0]] = originalTile2; } else { // If there was no original tile, delete the sprite references delete layer.sprites[undome[i][0]]; delete layer.composite_sprites[undome[i][0]]; } } } else if (event.shiftKey && event.code == 'ArrowUp') { g_ctx.tileset.fudgey -= 1; redrawGrid(g_ctx.tileset, true); } else if (event.shiftKey && event.code == 'ArrowDown') { g_ctx.tileset.fudgey += 1; redrawGrid(g_ctx.tileset, true); } else if (event.shiftKey && event.code == 'ArrowLeft') { g_ctx.tileset.fudgex -= 1; redrawGrid(g_ctx.tileset, true); } else if (event.shiftKey && event.code == 'ArrowRight') { g_ctx.tileset.fudgex += 1; redrawGrid(g_ctx.tileset, true); } } ); // Listen to pointermove on stage once handle is pressed. function onTilesetDragStart(e) { if (g_ctx.debug_flag) { console.log("onDragStartTileset()"); } g_ctx.tileset.app.stage.eventMode = 'static'; g_ctx.tileset.app.stage.addEventListener('pointermove', onTilesetDrag); g_ctx.tileset.dragctx.startx = e.data.global.x; g_ctx.tileset.dragctx.starty = e.data.global.y; g_ctx.tileset.dragctx.endx = e.data.global.x; g_ctx.tileset.dragctx.endy = e.data.global.y; g_ctx.tileset.app.stage.addChild(g_ctx.tileset.dragctx.square); // g_ctx.tileset.app.stage.addChild(g_ctx.tileset.dragctx.tooltip); g_ctx.selected_tiles = []; } // Stop dragging feedback once the handle is released. function onTilesetDragEnd(e) { if (g_ctx.debug_flag) { console.log("onDragEndTileset()"); } g_ctx.tileset.app.stage.eventMode = 'auto'; g_ctx.tileset.app.stage.removeEventListener('pointermove', onTilesetDrag); g_ctx.tileset.app.stage.removeChild(g_ctx.tileset.dragctx.square); g_ctx.tileset.app.stage.removeChild(g_ctx.tileset.dragctx.tooltip); if(g_ctx.tileset.dragctx.endx < g_ctx.tileset.dragctx.startx){ let tmp = g_ctx.tileset.dragctx.endx; g_ctx.tileset.dragctx.endx = g_ctx.tileset.dragctx.startx; g_ctx.tileset.dragctx.startx = tmp; } if(g_ctx.tileset.dragctx.endy < g_ctx.tileset.dragctx.starty){ let tmp = g_ctx.tileset.dragctx.endy; g_ctx.tileset.dragctx.endy = g_ctx.tileset.dragctx.starty; g_ctx.tileset.dragctx.starty = tmp; } let starttilex = Math.floor(g_ctx.tileset.dragctx.startx / g_ctx.tiledimx); let starttiley = Math.floor(g_ctx.tileset.dragctx.starty / g_ctx.tiledimx); let endtilex = Math.floor(g_ctx.tileset.dragctx.endx / g_ctx.tiledimx); let endtiley = Math.floor(g_ctx.tileset.dragctx.endy / g_ctx.tiledimx); if (g_ctx.debug_flag) { console.log("sx sy ex ey ", starttilex, ",", starttiley, ",", endtilex, ",", endtiley); } // let mouse clicked handle if there isn't a multiple tile square if(starttilex === endtilex && starttiley === endtiley ){ return; } // g_ctx.tile_index = (starttiley * g_ctx.tilesettilew) + starttilex; g_ctx.tile_index = tileset_index_from_px(e.global.x, e.global.y); let origx = starttilex; let origy = starttiley; for(let y = starttiley; y <= endtiley; y++){ for(let x = starttilex; x <= endtilex; x++){ let squareindex = (y * g_ctx.tilesettilew) + x; g_ctx.selected_tiles.push([x - origx,y - origy,squareindex]); } } g_ctx.tileset.dragctx.square.clear(); // g_ctx.tileset.dragctx.tooltip.clear(); } function onTilesetDrag(e) { if (g_ctx.debug_flag) { console.log("onDragTileset()"); } g_ctx.tileset.dragctx.endx = e.global.x; g_ctx.tileset.dragctx.endy = e.global.y; g_ctx.tileset.dragctx.square.clear(); g_ctx.tileset.dragctx.square.beginFill(0xFF3300, 0.3); g_ctx.tileset.dragctx.square.lineStyle(2, 0xffd900, 1); g_ctx.tileset.dragctx.square.moveTo(g_ctx.tileset.dragctx.startx, g_ctx.tileset.dragctx.starty); g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.endx, g_ctx.tileset.dragctx.starty); g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.endx, g_ctx.tileset.dragctx.endy); g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.startx, g_ctx.tileset.dragctx.endy); g_ctx.tileset.dragctx.square.closePath(); g_ctx.tileset.dragctx.square.endFill(); // g_ctx.tileset.dragctx.tooltip.clear(); // g_ctx.tileset.dragctx.tooltip.beginFill(0xFF3300, 0.3); // g_ctx.tileset.dragctx.tooltip.lineStyle(2, 0xffd900, 1); // g_ctx.tileset.dragctx.tooltip.drawRect(e.global.x, e.global.y, 20,8); // g_ctx.tileset.dragctx.tooltip.endFill(); } //g_ctx.tileset.app.stage.addChild(g_ctx.tileset.container); function redrawGrid(pane, redraw = false) { if (typeof pane.gridtoggle == 'undefined') { // first time we're being called, initialized pane.gridtoggle = false; pane.gridvisible = false; redraw = true; pane.gridvisible = true; } if (redraw) { if (typeof pane.gridgraphics != 'undefined') { pane.container.removeChild(pane.gridgraphics); } pane.gridgraphics = new PIXI.Graphics(); let gridsizex = g_ctx.tiledimx; let gridsizey = g_ctx.tiledimy; pane.gridgraphics.lineStyle(1, 0x000000, 1); let index = 0; for (let i = 0; i < pane.widthpx; i += gridsizex) { pane.gridgraphics.moveTo(i + pane.fudgex, 0 + pane.fudgey); pane.gridgraphics.lineTo(i + pane.fudgex, pane.heightpx + pane.fudgey); pane.gridgraphics.moveTo(i + gridsizex + pane.fudgex, 0 + pane.fudgey); pane.gridgraphics.lineTo(i + gridsizex + pane.fudgex, pane.heightpx + pane.fudgey); } for (let j = 0; j < pane.heightpx; j += gridsizey) { pane.gridgraphics.moveTo(0 + pane.fudgex, j + gridsizey + pane.fudgey); pane.gridgraphics.lineTo(pane.widthpx + pane.fudgex, j + gridsizey + pane.fudgey); pane.gridgraphics.moveTo(0 + pane.fudgex, j + pane.fudgey); pane.gridgraphics.lineTo(pane.heightpx + pane.fudgex, j + pane.fudgey); } if(pane.gridvisible){ pane.container.addChild(pane.gridgraphics); } return; } if (pane.gridtoggle) { pane.container.addChild(pane.gridgraphics); pane.gridvisible = true; }else{ pane.container.removeChild(pane.gridgraphics); pane.gridvisible = false; } pane.gridtoggle = !pane.gridtoggle; } // -- // Variable placement logic Level1 // -- function centerCompositePane(x, y){ var compositepane = document.getElementById("compositepane"); compositepane.scrollLeft = x - (CONFIG.htmlCompositePaneW/2); compositepane.scrollTop = y - (CONFIG.htmlCompositePaneH/2); } function getOldTileValue(layer, x, y) { let levelIndex = level_index_from_px(x, y); return layer.sprites[levelIndex] ? layer.sprites[levelIndex].index : -1; } function centerLayerPanes(x, y){ // TODO remove magic number pulled from index.html g_ctx.g_layers.map((l) => { l.scrollpane.scrollLeft = x - (CONFIG.htmlLayerPaneW/2); l.scrollpane.scrollTop = y - (CONFIG.htmlLayerPaneH/2); }); } function onLevelMouseover(e) { let x = e.data.global.x; let y = e.data.global.y; if(g_ctx.debug_flag2){ console.log("onLevelMouseOver ",this.num); } if (x < this.scrollpane.scrollLeft || x > this.scrollpane.scrollLeft + CONFIG.htmlCompositePaneW) { return; } if (y < this.scrollpane.scrollTop || y > this.scrollpane.scrollTop + CONFIG.htmlCompositePaneH) { return; } // FIXME test code if ( g_ctx.spritesheet != null){ let ctile = new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']); let ctile2 = new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']); ctile.animationSpeed = .1; ctile2.animationSpeed = .1; ctile.autoUpdate = true; ctile2.autoUpdate = true; ctile.alpha = .5; ctile2.alpha = .5; ctile.play(); ctile2.play(); this.mouseshadow.addChild(ctile); g_ctx.composite.mouseshadow.addChild(ctile2); // FIXME test code } else if (this.lasttileindex != g_ctx.tile_index) { this.mouseshadow.removeChildren(0); g_ctx.composite.mouseshadow.removeChildren(0); if (g_ctx.selected_tiles.length == 0) { let shadowsprite = null; let shadowsprite2 = null; let pxloc = tileset_px_from_index(g_ctx.tile_index); shadowsprite = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); shadowsprite2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); shadowsprite.alpha = .5; shadowsprite2.alpha = .5; this.mouseshadow.addChild(shadowsprite); g_ctx.composite.mouseshadow.addChild(shadowsprite2); } else { // TODO! adjust for fudge for (let i = 0; i < g_ctx.selected_tiles.length; i++) { let tile = g_ctx.selected_tiles[i]; let pxloc = tileset_px_from_index(tile[2]); const shadowsprite = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); const shadowsprite2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); shadowsprite.x = tile[0] * g_ctx.tiledimx; shadowsprite.y = tile[1] * g_ctx.tiledimx; shadowsprite2.x = tile[0] * g_ctx.tiledimx; shadowsprite2.y = tile[1] * g_ctx.tiledimx; shadowsprite.alpha = .5; shadowsprite2.alpha = .5; this.mouseshadow.addChild(shadowsprite); g_ctx.composite.mouseshadow.addChild(shadowsprite2); } } this.mouseshadow.x = x - 16; this.mouseshadow.y = y - 16; this.container.removeChild(this.mouseshadow); g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow); this.container.addChild(this.mouseshadow); g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow); } g_ctx.composite.app.stage.removeChild(g_ctx.composite.circle); g_ctx.composite.app.stage.addChild(g_ctx.composite.circle); } function onLevelMouseOut(e) { if (g_ctx.debug_flag2) { console.log("onLevelMouseOut ",this.num); } //FIXME there is a funky race condition where the mouse enters a second layer before leaving the last and the following line //deletes the composite mouseshadow. I'm not quite sure how to solve without mapping the composite.mouseshadow to each layer this.mouseshadow.removeChildren(0); g_ctx.composite.mouseshadow.removeChildren(); } function onLevelMousemove(e) { let x = e.data.global.x; let y = e.data.global.y; // FIXME TEST CODE this.mouseshadow.x = x-8; this.mouseshadow.y = y-8; g_ctx.composite.mouseshadow.x = x-8; g_ctx.composite.mouseshadow.y = y-8; // FIXME TEST CODE if (x < this.scrollpane.scrollLeft || x > this.scrollpane.scrollLeft + CONFIG.htmlCompositePaneW) { return; } if (y < this.scrollpane.scrollTop || y > this.scrollpane.scrollTop + CONFIG.htmlCompositePaneH) { return; } g_ctx.composite.circle.clear(); g_ctx.composite.circle.beginFill(0xe50000, 0.5); g_ctx.composite.circle.drawCircle(e.data.global.x, e.data.global.y, 3); g_ctx.composite.circle.endFill(); } function onCompositeMousedown(layer, e) { if (g_ctx.debug_flag) { console.log('onCompositeMouseDown: X', e.data.global.x, 'Y', e.data.global.y); } let xorig = e.data.global.x; let yorig = e.data.global.y; centerLayerPanes(xorig,yorig); } // Place with no variable target at destination function levelPlaceNoVariable(layer, e) { if (g_ctx.debug_flag) { console.log('levelPlaceNoVariable: X', e.data.global.x, 'Y', e.data.global.y); } let xorig = e.data.global.x; let yorig = e.data.global.y; centerCompositePane(xorig, yorig); if (g_ctx.dkey || g_ctx.selected_tiles.length == 0) { let oldValue = getOldTileValue(layer, e.data.global.x, e.data.global.y); let ti = layer.addTileLevelPx(e.data.global.x, e.data.global.y, g_ctx.tile_index); UNDO.undo_add_single_index_as_task(layer, ti, oldValue); } else { UNDO.undo_mark_task_start(layer); for (let index of g_ctx.selected_tiles) { // Calculate position and get old value let x = xorig + index[0] * g_ctx.tiledimx; let y = yorig + index[1] * g_ctx.tiledimx; let oldValue = getOldTileValue(layer, x, y); let ti = layer.addTileLevelPx(x, y, index[2]); UNDO.undo_add_index_to_task(ti, oldValue); } UNDO.undo_mark_task_end(); } } // Listen to pointermove on stage once handle is pressed. function onLevelPointerDown(layer, e) { if (g_ctx.debug_flag) { console.log("onLevelPointerDown()"); } layer.app.stage.eventMode = 'static'; layer.app.stage.addEventListener('pointermove', onLevelDrag.bind(null, layer, e)); layer.container.removeChild(layer.mouseshadow); g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow); layer.dragctx.startx = e.data.global.x; layer.dragctx.starty = e.data.global.y; layer.dragctx.endx = e.data.global.x; layer.dragctx.endy = e.data.global.y; layer.app.stage.addChild(layer.dragctx.square); layer.app.stage.addChild(layer.dragctx.tooltip); } function onLevelDrag(layer, e) { if(layer.dragctx.startx == -1){ layer.dragctx.square.clear(); return; } layer.dragctx.endx = e.global.x; layer.dragctx.endy = e.global.y; if (g_ctx.debug_flag) { console.log("onLevelDrag()"); } layer.dragctx.square.clear(); layer.dragctx.square.beginFill(0xFF3300, 0.3); layer.dragctx.square.lineStyle(2, 0xffd900, 1); layer.dragctx.square.moveTo(layer.dragctx.startx, layer.dragctx.starty); layer.dragctx.square.lineTo(layer.dragctx.endx, layer.dragctx.starty); layer.dragctx.square.lineTo(layer.dragctx.endx, layer.dragctx.endy); layer.dragctx.square.lineTo(layer.dragctx.startx, layer.dragctx.endy); layer.dragctx.square.closePath(); layer.dragctx.square.endFill(); const vwidth = Math.floor((layer.dragctx.endx - layer.dragctx.startx)/g_ctx.tiledimx); const vheight = Math.floor((layer.dragctx.endy - layer.dragctx.starty)/g_ctx.tiledimx); layer.dragctx.tooltip.x = e.global.x + 16; layer.dragctx.tooltip.y = e.global.y - 4; layer.dragctx.tooltip.text = "["+vwidth+","+vheight+"]\n"+ "("+Math.floor(e.global.x/g_ctx.tiledimx)+","+Math.floor(e.global.y/g_ctx.tiledimx)+")"; //layer.dragctx.tooltip.text = "("+e.global.x+","+e.global.y+")"; } // Stop dragging feedback once the handle is released. function onLevelDragEnd(layer, e) { layer.dragctx.endx = e.data.global.x; layer.dragctx.endy = e.data.global.y; if(layer.dragctx.startx == -1){ console.log("onLevelDragEnd() start is -1 bailing"); return; } if (g_ctx.debug_flag) { console.log("onLevelDragEnd()"); } if(layer.dragctx.endx < layer.dragctx.startx){ let tmp = layer.dragctx.endx; layer.dragctx.endx = layer.dragctx.startx; layer.dragctx.startx = tmp; } if(layer.dragctx.endy < layer.dragctx.starty){ let tmp = layer.dragctx.endy; layer.dragctx.endy = layer.dragctx.starty; layer.dragctx.starty = tmp; } //FIXME TEST CODE show mouseshadow again once done draggin layer.container.addChild(layer.mouseshadow); g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow); layer.app.stage.eventMode = 'auto'; layer.app.stage.removeChild(layer.dragctx.square); layer.app.stage.removeChild(layer.dragctx.tooltip); let starttilex = Math.floor(layer.dragctx.startx / g_ctx.tiledimx); let starttiley = Math.floor(layer.dragctx.starty / g_ctx.tiledimx); let endtilex = Math.floor(layer.dragctx.endx / g_ctx.tiledimx); let endtiley = Math.floor(layer.dragctx.endy / g_ctx.tiledimx); if (g_ctx.debug_flag) { console.log("sx ", starttilex, " ex ", endtilex); console.log("sy ", starttiley, " ey ", endtiley); } // no variable placement. if(starttilex === endtilex && starttiley == endtiley ){ levelPlaceNoVariable(layer, e); layer.dragctx.startx = -1; layer.dragctx.endx = -1; layer.dragctx.starty = -1; layer.dragctx.endy = -1; return; } if (g_ctx.selected_tiles.length == 0) { UNDO.undo_mark_task_start(layer); for (let i = starttilex; i <= endtilex; i++) { for (let j = starttiley; j <= endtiley; j++) { let x = i * g_ctx.tiledimx; let y = j * g_ctx.tiledimx; let oldValue = getOldTileValue(layer, x, y); let ti = layer.addTileLevelPx(x, y, g_ctx.tile_index); UNDO.undo_add_index_to_task(ti, oldValue); } } UNDO.undo_mark_task_end(); } else { // figure out selected grid let selected_grid = Array.from(Array(64), () => new Array(64)); // FIXME ... hope 64x64 is enough let row = 0; let column = 0; let selected_row = g_ctx.selected_tiles[0][1]; // selected_grid[0] = []; for (let index of g_ctx.selected_tiles) { // console.log("Selected row ", selected_row, index); if(index[1] != selected_row){ selected_row = index[1]; row++; column = 0; //selected_grid[row] = []; } selected_grid[column++][row] = index; } // at this point should have a 3D array of the selected tiles and the size should be row, column UNDO.undo_mark_task_start(layer); let ti=0; for (let i = starttilex; i <= endtilex; i++) { for (let j = starttiley; j <= endtiley; j++) { // Get the old value before placing new tile let x = i * g_ctx.tiledimx; let y = j * g_ctx.tiledimx; let oldValue = getOldTileValue(layer, x, y); if (j === starttiley) { // first row if (i === starttilex) { // top left corner ti = layer.addTileLevelPx(x, y, selected_grid[0][0][2]); } else if (i == endtilex) { // top right corner ti = layer.addTileLevelPx(x, y, selected_grid[column - 1][0][2]); } else { // top middle ti = layer.addTileLevelPx(x, y, selected_grid[1][0][2]); } } else if (j === endtiley) { // last row if (i === starttilex) { // bottom left corner ti = layer.addTileLevelPx(x, y, selected_grid[0][row][2]); } else if (i == endtilex) { // bottom right corner ti = layer.addTileLevelPx(x, y, selected_grid[column - 1][row][2]); } else { // bottom middle ti = layer.addTileLevelPx(x, y, selected_grid[1][row][2]); } } else { // middle row if (i === starttilex) { // middle left ti = layer.addTileLevelPx(x, y, selected_grid[0][(row > 0)? 1 : 0][2]); } else if (i === endtilex) { // middle end ti = layer.addTileLevelPx(x, y, selected_grid[column - 1][(row > 0)? 1 : 0][2]); } else { // middle middle ti = layer.addTileLevelPx(x, y, selected_grid[1][(row > 0)? 1 : 0][2]); } } UNDO.undo_add_index_to_task(ti, oldValue); } } UNDO.undo_mark_task_end(); } layer.dragctx.square.clear(); layer.dragctx.startx = -1; layer.dragctx.starty = -1; } // -- // Initialized all pixi apps / components for application // -- function initPixiApps() { // -- Editor wide globals -- // First layer of level const level_app0 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level0') }); let layer0 = new LayerContext(level_app0, document.getElementById("layer0pane"), 0); // second layer of level const level_app1 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level1') }); let layer1 = new LayerContext(level_app1, document.getElementById("layer1pane"), 1); // object layer of level const level_app2 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level3') }); let layer2 = new LayerContext(level_app2, document.getElementById("layer2pane"), 2); // object layer of level const level_app3 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level4') }); let layer3 = new LayerContext(level_app3, document.getElementById("layer3pane"), 3); g_ctx.g_layer_apps = []; g_ctx.g_layer_apps.push(level_app0 ); g_ctx.g_layer_apps.push(level_app1); g_ctx.g_layer_apps.push(level_app2); g_ctx.g_layer_apps.push(level_app3); g_ctx.g_layers = []; g_ctx.g_layers.push(layer0); g_ctx.g_layers.push(layer1); g_ctx.g_layers.push(layer2); g_ctx.g_layers.push(layer3); // g_ctx.composite view g_ctx.composite_app = new PIXI.Application({ backgroundAlpha: 0, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('composite') }); g_ctx.composite = new CompositeContext(g_ctx.composite_app); // map tab g_ctx.map_app = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('mapcanvas') }); // g_ctx.tileset g_ctx.tileset_app = new PIXI.Application({ width: 5632 , height: 8672, view: document.getElementById('tileset') }); //g_ctx.tileset_app = new PIXI.Application({ width: g_ctx.tilesetpxw, height: g_ctx.tilesetpxh, view: document.getElementById('tileset') }); // const { renderer } = g_ctx.tileset_app; // // Install the EventSystem // renderer.addSystem(EventSystem, 'tileevents'); g_ctx.tileset = new TilesetContext(g_ctx.tileset_app); } function setGridSize(size) { if (size == 16) { if (g_ctx.tiledimx == 16) { return; } g_ctx.tilesettilew = (g_ctx.tilesettilew/ (size / g_ctx.tiledimx)); g_ctx.tilesettileh = (g_ctx.tilesettileh / (size / g_ctx.tiledimy)); g_ctx.tiledimx = 16; g_ctx.tiledimy = 16; g_ctx.curtiles = g_ctx.tiles16; console.log("set to curTiles16"); } else if (size == 32) { if (g_ctx.tiledimx == 32) { return; } g_ctx.tilesettilew = (g_ctx.tilesettilew/ (size / g_ctx.tiledimx)); g_ctx.tilesettileh = (g_ctx.tilesettileh / (size / g_ctx.tiledimy)); g_ctx.tiledimx = 32; g_ctx.tiledimy = 32; g_ctx.curtiles = g_ctx.tiles32; console.log("set to curTiles32"); } else { console.debug("Invalid TileDim!"); return; } g_ctx.g_layers.map((l) => redrawGrid (l, true) ); redrawGrid(g_ctx.tileset, true); redrawGrid(g_ctx.composite, true); } function initRadios() { var rad = document.myForm.radioTiledim; var prev = null; for (var i = 0; i < rad.length; i++) { rad[i].addEventListener('change', function () { if (this !== prev) { prev = this; } setGridSize(this.value); }); } } // -- // Load in default tileset and use to set properties // -- function initTilesSync(callme) { return new Promise((resolve, reject) => { console.log("initTileSync"); const texture = new PIXI.BaseTexture(g_ctx.tilesetpath); if(texture.valid) { console.log("BaseTexture already valid"); callme(); return; } console.log("Loading texture ", g_ctx.tilesetpath); texture.on('loaded', function () { // size of g_ctx.tileset in px g_ctx.tilesetpxw = texture.width; g_ctx.tilesetpxh = texture.height; console.log("Texture size w:", g_ctx.tilesetpxw, "h:", g_ctx.tilesetpxh); // size of g_ctx.tileset in tiles let tileandpad = g_ctx.tiledimx + CONFIG.tilesetpadding; let numtilesandpadw = Math.floor(g_ctx.tilesetpxw / tileandpad); g_ctx.tilesettilew = numtilesandpadw + Math.floor((g_ctx.tilesetpxw - (numtilesandpadw * tileandpad)) / g_ctx.tiledimx); let numtilesandpadh = Math.floor(g_ctx.tilesetpxh / tileandpad); g_ctx.tilesettileh = numtilesandpadh + Math.floor((g_ctx.tilesetpxh - (numtilesandpadh * tileandpad)) / g_ctx.tiledimx); console.log("Number of x tiles ", g_ctx.tilesettilew, " y tiles ", g_ctx.tilesettileh); g_ctx.MAXTILEINDEX = g_ctx.tilesettilew * g_ctx.tilesettileh; texture.destroy(); resolve(); callme(); }); }); } // -- // Load default Tileset // -- const initTilesConfig = async () => { g_ctx.tilesetpath = CONFIG.DEFAULTTILESETPATH; return new Promise((resolve, reject) => { const texture = new PIXI.BaseTexture(g_ctx.tilesetpath); if (g_ctx.debug_flag) { console.log("initTilessConfi: Loading texture ",g_ctx.tilesetpath); } texture .on('loaded', function() { // size of g_ctx.tileset in px g_ctx.tilesetpxw = texture.width; g_ctx.tilesetpxh = texture.height; if (g_ctx.debug_flag) { console.log("\tsize w:", g_ctx.tilesetpxw, "h:", g_ctx.tilesetpxh); } // size of g_ctx.tileset in tiles let tileandpad = g_ctx.tiledimx + CONFIG.tilesetpadding; let numtilesandpadw = Math.floor(g_ctx.tilesetpxw / tileandpad); g_ctx.tilesettilew = numtilesandpadw + Math.floor((g_ctx.tilesetpxw - (numtilesandpadw * tileandpad))/g_ctx.tiledimx); let numtilesandpadh = Math.floor(g_ctx.tilesetpxh / tileandpad); g_ctx.tilesettileh = numtilesandpadh + Math.floor((g_ctx.tilesetpxh - (numtilesandpadh * tileandpad))/g_ctx.tiledimx); if (g_ctx.debug_flag) { console.log("\tnum tiles x ", g_ctx.tilesettilew, " y ", g_ctx.tilesettileh); } g_ctx.MAXTILEINDEX = g_ctx.tilesettilew * g_ctx.tilesettileh; texture.destroy(); resolve(); }); }); }; function initTiles() { // load g_ctx.tileset into a global array of textures for blitting onto levels const bt = PIXI.BaseTexture.from(g_ctx.tilesetpath, { scaleMode: PIXI.SCALE_MODES.NEAREST, }); for (let x = 0; x < CONFIG.tilesettilewidth; x++) { for (let y = 0; y < CONFIG.tilesettileheight; y++) { g_ctx.tiles32[x + y * CONFIG.tilesettilewidth] = new PIXI.Texture( bt, new PIXI.Rectangle(x * 32, y * 32, 32, 32), ); } } for (let x = 0; x < CONFIG.tilesettilewidth * 2; x++) { for (let y = 0; y < CONFIG.tilesettileheight * 2; y++) { g_ctx.tiles16[x + y * CONFIG.tilesettilewidth * 2] = new PIXI.Texture( bt, new PIXI.Rectangle(x * 16, y * 16, 16, 16), ); } } g_ctx.curtiles = g_ctx.tiles32; } async function init() { UI.initMainHTMLWindow(); // We need to load the Tileset to know how to size things. So we block until done. await initTilesConfig(); initPixiApps(); initRadios(); initTiles(); UI.initLevelLoader(loadMapFromModule); UI.initCompositePNGLoader(); UI.initSpriteSheetLoader(); UI.initTilesetLoader( loadMapFromModule.bind(null, g_ctx)); } init(); ================================================ FILE: src/editor/leconfig.js ================================================ export const DEFAULTTILESETPATH = "./tilesets/gentle.png"; //export const DEFAULTTILESETPATH = "./tilesets/magecity.png"; //export const DEFAULTTILESETPATH = "./tilesets/forest.png"; //export const DEFAULTTILESETPATH = "./tilesets/Serene.png"; //export const DEFAULTTILESETPATH = "./tilesets/gentletreewall.png"; //export const DEFAULTTILESETPATH = "./tilesets/Modern.png"; //export const DEFAULTTILESETPATH = "./tilesets/phantasy2.png"; export const tilesetpadding = 0; export const DEFAULTILEDIMX = 32; // px export const DEFAULTILEDIMY = 32; // px export const levelwidth = 2048; // px export const levelheight = 1536; // px export let leveltilewidth = Math.floor(levelwidth / DEFAULTILEDIMX); export let leveltileheight = Math.floor(levelheight / DEFAULTILEDIMX); export const MAXTILEINDEX = leveltilewidth * leveltileheight; // -- HTML export const htmlLayerPaneW = 800; export const htmlLayerPaneH = 600 export const htmlTilesetPaneW = 800; export const htmlTilesetPaneH = 600; export const htmlCompositePaneW = 800; export const htmlCompositePaneH = 600; // -- zIndex // 1-10 taken by layers export const zIndexFilter = 20; export const zIndexMouseShadow = 30; export const zIndexGrid = 50; export const zIndexCompositePointer = 100; ================================================ FILE: src/editor/lecontext.js ================================================ import * as PIXI from 'pixi.js' import * as CONFIG from './leconfig.js' var ContextCreate = (function(){ function ContextSingleton() { this.tilesetpxw = 0; this.tilesetpxh = 0; this.tilesettilew = 0; this.tilesettileh = 0; this.MAXTILEINDEX = 0; this.tile_index = 0; this.selected_tiles = []; // current set of selected tiles this.spritesheet = null; // loaded spritesheet this.tiledimx = CONFIG.DEFAULTILEDIMX ; // px this.tiledimy = CONFIG.DEFAULTILEDIMY; // px this.dimlog = Math.log2(this.tileDim); //log2(TileDim) this.dkey = false; // is 'd' key depressed? (for delete) this.tiles32 = []; // all tiles from tilemap (32x32) this.tiles16 = []; this.fudgetiles = []; this.g_layers = []; // level layers } var instance; return { getInstance: function(){ if (instance == null) { instance = new ContextSingleton(); // Hide the constructor so the returned object can't be new'd... instance.constructor = null; } return instance; } }; })(); // global shared state between all panes export let g_ctx = ContextCreate.getInstance(); ================================================ FILE: src/editor/lehtmlui.js ================================================ import * as PIXI from 'pixi.js' import { g_ctx } from './lecontext.js' // global context import * as CONFIG from './leconfig.js' // -- // Set sizes and limits for HTML in main UI // -- export function initMainHTMLWindow() { document.getElementById("layer0pane").style.maxWidth = ""+CONFIG.htmlLayerPaneW+"px"; document.getElementById("layer0pane").style.maxHeight = ""+CONFIG.htmlLayerPaneH+"px"; document.getElementById("layer1pane").style.maxWidth = ""+CONFIG.htmlLayerPaneW+"px"; document.getElementById("layer1pane").style.maxHeight = ""+CONFIG.htmlLayerPaneH+"px"; document.getElementById("layer2pane").style.maxWidth = ""+CONFIG.htmlLayerPaneW+"px"; document.getElementById("layer2pane").style.maxHeight = ""+CONFIG.htmlLayerPaneH+"px"; document.getElementById("layer3pane").style.maxWidth = ""+CONFIG.htmlLayerPaneW+"px"; document.getElementById("layer3pane").style.maxHeight = ""+CONFIG.htmlLayerPaneH+"px"; document.getElementById("tilesetpane").style.maxWidth = ""+CONFIG.htmlTilesetPaneW+"px"; document.getElementById("tilesetpane").style.maxHeight = ""+CONFIG.htmlTilesetPaneH+"px"; document.getElementById("compositepane").style.maxWidth = ""+CONFIG.htmlCompositePaneW+"px"; document.getElementById("compositepane").style.maxHeight = ""+CONFIG.htmlCompositePaneH+"px"; // hide map tab let mappane = document.getElementById("map"); mappane.style.display = "none"; } // -- // Initialize handlers for file loading // -- // -- // Initialize handlers loading a PNG file into the composite window // -- export function initCompositePNGLoader() { const fileInput = document.getElementById('compositepng'); fileInput.onchange = (evt) => { if (!window.FileReader) return; // Browser is not compatible if (g_ctx.debug_flag) { console.log("compositepng ", fileInput.files[0].name); } let bgname = fileInput.files[0].name; const texture = PIXI.Texture.from("./tilesets/"+bgname); const bg = new PIXI.Sprite(texture); bg.zIndex = 0; g_ctx.composite.container.addChild(bg); } } // -- // initailized handler to load a spriteSheet into current working tile // -- export function initSpriteSheetLoader() { const fileInput = document.getElementById('spritesheet'); fileInput.onchange = async (evt) => { if (!window.FileReader) return; // Browser is not compatible if (g_ctx.debug_flag) { console.log("spritesheet ", fileInput.files[0].name); } let ssname = fileInput.files[0].name; let sheet = await PIXI.Assets.load("./"+ssname); console.log(sheet); g_ctx.tileset.addTileSheet(ssname, sheet); g_ctx.selected_tiles = []; } } // -- // initailized handler to load a new tileset // -- export function initTilesetLoader(callme) { const fileInput = document.getElementById('tilesetfile'); fileInput.onchange = async (evt) => { if (!window.FileReader) return; // Browser is not compatible if (g_ctx.debug_flag) { console.log("tilesetfile ", fileInput.files[0].name); } g_ctx.tilesetpath = "./tilesets/"+fileInput.files[0].name; callme(); } } // -- // initailized handler to load a level from a file // -- function doimport (str) { if (globalThis.URL.createObjectURL) { const blob = new Blob([str], { type: 'text/javascript' }) const url = URL.createObjectURL(blob) const module = import(url) URL.revokeObjectURL(url) // GC objectURLs return module } const url = "data:text/javascript;base64," + btoa(moduleData) return import(url) } export function initLevelLoader(callme) { let filecontent = ""; const fileInput = document.getElementById('levelfile'); fileInput.onchange = (evt) => { if (!window.FileReader) return; // Browser is not compatible var reader = new FileReader(); reader.onload = function (evt) { if (evt.target.readyState != 2) return; if (evt.target.error) { alert('Error while reading file'); return; } filecontent = evt.target.result; doimport(filecontent).then(mod => callme(mod)); }; reader.readAsText(evt.target.files[0]); } } ================================================ FILE: src/editor/mapfile.js ================================================ import * as CONFIG from './leconfig.js' import * as UTIL from './eutils.js' import { g_ctx } from './lecontext.js' // global context function generate_preamble() { const mapfile_preamble = '' + '// Map generated by assettool.js [' + new Date() + ']\n' + '\n' + 'export const tilesetpath = "' + g_ctx.tilesetpath + '"\n' + 'export const tiledim = ' + g_ctx.tiledimx + '\n' + 'export const screenxtiles = ' + g_ctx.tilesettilew + '\n' + 'export const screenytiles = ' + g_ctx.tilesettileh + '\n' + 'export const tilesetpxw = ' + g_ctx.tilesetpxw + '\n' + 'export const tilesetpxh = ' + g_ctx.tilesetpxh + '\n\n' return mapfile_preamble; } const bgtile_string_start = '' + 'export const bgtiles = [\n' + ' [\n' function write_map_file(bg_tiles_0, bg_tiles_1, obj_tiles_1, obj_tiles_2, animated_tiles){ let text = generate_preamble(); text += bgtile_string_start; for(let row = 0; row < bg_tiles_0.length; row++) { text += '[ '; for(let column = 0; column < bg_tiles_0[row].length; column++) { text += bg_tiles_0[row][column]; if (column != bg_tiles_0.length - 1){ text += ' , '; } } text += '],\n'; } text += '],\n'; text += '[\n'; for(let row = 0; row < bg_tiles_1.length; row++) { text += '[ '; for(let column = 0; column < bg_tiles_1[row].length; column++) { text += bg_tiles_1[row][column]; if (column != bg_tiles_1.length - 1){ text += ' , '; } } text += '],\n'; } text += '],];\n\n'; text += ''+ 'export const objmap = [\n'+ '[\n'; for(let row = 0; row < obj_tiles_1.length; row++) { text += '[ '; for(let column = 0; column < obj_tiles_1[row].length; column++) { text += obj_tiles_1[row][column]; if (column != obj_tiles_1.length - 1){ text += ' , '; } } text += '],\n'; } text += '],\n'; text += '[\n'; for(let row = 0; row < obj_tiles_2.length; row++) { text += '[ '; for(let column = 0; column < obj_tiles_2[row].length; column++) { text += obj_tiles_2[row][column]; if (column != obj_tiles_2.length - 1){ text += ' , '; } } text += '],\n'; } text += '],];\n'; text += ''+ 'export const animatedsprites = [\n'; for(let x = 0 ; x < animated_tiles.length; x++){ let atile = animated_tiles[x]; text += '{ x: '+atile.x+", y: "+ atile.y+ ", w: "+ atile.width+ ", h: "+ atile.height ; text += ', layer: '+atile.layer; text += ', sheet: "'+ atile.spritesheetname+ '", animation: "'+ atile.animationname+'" },\n'; } text += '];\n\n'; text += 'export const mapwidth = bgtiles[0][0].length;\n'; text += 'export const mapheight = bgtiles[0].length;\n'; UTIL.download(text, "map.js", "text/plain"); } export function generate_level_file() { let layer0 = g_ctx.g_layers[0]; let layer1 = g_ctx.g_layers[1]; let layer2 = g_ctx.g_layers[2]; let layer3 = g_ctx.g_layers[3]; let animated_tiles = []; // level0 var tile_array0 = Array.from(Array(CONFIG.leveltilewidth), () => new Array(CONFIG.leveltileheight)); for (let x = 0; x < CONFIG.leveltilewidth; x++) { for (let y = 0; y < CONFIG.leveltileheight; y++) { tile_array0[x][y] = -1; } } for (var i = 0; i < layer0.container.children.length; i++) { var child = layer0.container.children[i]; // check if it's an animated sprite if(child.hasOwnProperty('animationSpeed')){ child.layer = 0; animated_tiles.push(child); continue; } if (!child.hasOwnProperty('index')) { continue; } let x_coord = child.x / g_ctx.tiledimx; let y_coord = child.y / g_ctx.tiledimy; if (typeof tile_array0[x_coord] == 'undefined'){ console.log("**Error xcoord undefined ", x_coord); } else if (typeof tile_array0[x_coord][y_coord] == 'undefined'){ console.log("**Error xcoord/ycoord undefined ", x_coord, y_coord); }else{ tile_array0[x_coord][y_coord] = child.index; } } // level1 var tile_array1 = Array.from(Array(CONFIG.leveltilewidth), () => new Array(CONFIG.leveltileheight)); for (let x = 0; x < CONFIG.leveltilewidth; x++) { for (let y = 0; y < CONFIG.leveltileheight; y++) { tile_array1[x][y] = -1; } } for (var i = 0; i < layer1.container.children.length; i++) { var child = layer1.container.children[i]; // check if it's an animated sprite if(child.hasOwnProperty('animationSpeed')){ child.layer = 1; animated_tiles.push(child); continue; } if (!child.hasOwnProperty('index')) { continue; } let x_coord = child.x / g_ctx.tiledimx; let y_coord = child.y / g_ctx.tiledimy; if (typeof tile_array1[x_coord] == 'undefined'){ console.log("**Error xcoord undefined ", x_coord); } else if (typeof tile_array1[x_coord][y_coord] == 'undefined'){ console.log("**Error xcoord/ycoord undefined ", x_coord, y_coord); }else{ tile_array1[x_coord][y_coord] = child.index; } } // object level var tile_array2 = Array.from(Array(CONFIG.leveltilewidth), () => new Array(CONFIG.leveltileheight)); for (let x = 0; x < CONFIG.leveltilewidth; x++) { for (let y = 0; y < CONFIG.leveltileheight; y++) { tile_array2[x][y] = -1; } } for (var i = 0; i < layer2.container.children.length; i++) { var child = layer2.container.children[i]; // check if it's an animated sprite if(child.hasOwnProperty('animationSpeed')){ child.layer = 2; animated_tiles.push(child); continue; } if (!child.hasOwnProperty('index')) { continue; } let x_coord = child.x / g_ctx.tiledimx; let y_coord = child.y / g_ctx.tiledimy; if (typeof tile_array2[x_coord] == 'undefined'){ console.log("**Error xcoord undefined ", x_coord); } else if (typeof tile_array2[x_coord][y_coord] == 'undefined'){ console.log("**Error xcoord/ycoord undefined ", x_coord, y_coord); }else{ tile_array2[x_coord][y_coord] = child.index; } } // object level var tile_array3 = Array.from(Array(CONFIG.leveltilewidth), () => new Array(CONFIG.leveltileheight)); for (let x = 0; x < CONFIG.leveltilewidth; x++) { for (let y = 0; y < CONFIG.leveltileheight; y++) { tile_array3[x][y] = -1; } } for (var i = 0; i < layer3.container.children.length; i++) { var child = layer3.container.children[i]; // check if it's an animated sprite if(child.hasOwnProperty('animationSpeed')){ child.layer = 3; animated_tiles.push(child); continue; } if (!child.hasOwnProperty('index')) { continue; } let x_coord = child.x / g_ctx.tiledimx; let y_coord = child.y / g_ctx.tiledimy; if (typeof tile_array3[x_coord] == 'undefined'){ console.log("**Error xcoord undefined ", x_coord); } else if (typeof tile_array3[x_coord][y_coord] == 'undefined'){ console.log("**Error xcoord/ycoord undefined ", x_coord, y_coord); }else{ tile_array3[x_coord][y_coord] = child.index; } } write_map_file(tile_array0, tile_array1, tile_array2, tile_array3, animated_tiles); } ================================================ FILE: src/editor/maps/gentle-full.js ================================================ // Map generated by assettool.js [Mon Oct 02 2023 00:20:59 GMT-0700 (Pacific Daylight Time)] export const tilesetpath = "./tilesets/gentle-obj.png" export const tiledim = 32 export const screenxtiles = 45 export const screenytiles = 32 export const tilesetpxw = 1440 export const tilesetpxh = 1024 export const bgtiles = [ [ [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], ], [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ],]; export const objmap = [ [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ], [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ],]; export const animatedsprites = [ { x: 736, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 256, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 224, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 224, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 192, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 192, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 160, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 128, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 96, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 64, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 736, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 512, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 512, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 544, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 576, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 736, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 768, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 800, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 832, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 1440, y: 352, w: 32, h: 32, layer: 1, sheet: "campfire.json", animation: "pixels_large" }, { x: 1664, y: 576, w: 208, h: 208, layer: 1, sheet: "windmill.json", animation: "pixels_large" }, { x: 1440, y: 768, w: 208, h: 208, layer: 1, sheet: "windmill.json", animation: "pixels_large" }, { x: 1120, y: 608, w: 208, h: 208, layer: 1, sheet: "windmill.json", animation: "pixels_large" }, { x: 768, y: 736, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 768, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 800, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 832, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 864, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 864, y: 1024, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1056, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 864, y: 1088, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1088, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1120, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1152, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1184, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 928, y: 1152, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 736, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 768, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 800, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 832, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, ]; ================================================ FILE: src/editor/maps/gentle.js ================================================ // Map generated by assettool.js [Wed Oct 18 2023 21:07:27 GMT-0700 (Pacific Daylight Time)] export const tilesetpath = "./tilesets/gentle-obj.png" export const tiledim = 32 export const screenxtiles = 45 export const screenytiles = 32 export const tilesetpxw = 1440 export const tilesetpxh = 1024 export const bgtiles = [ [ [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], [ 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 271 , 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 , ], ], [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ],]; export const objmap = [ [ [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ -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 , ], [ -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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], ], [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ],]; export const animatedsprites = [ { x: 1440, y: 352, w: 32, h: 32, layer: 1, sheet: "campfire.json", animation: "pixels_large" }, { x: 736, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 256, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 224, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 224, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 192, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 192, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 160, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 128, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 96, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 64, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 736, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 448, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 832, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 512, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 512, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 544, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 576, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 768, y: 736, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 768, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 800, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 832, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 800, y: 864, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 864, y: 1024, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1056, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 864, y: 1088, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1088, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1120, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1152, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 896, y: 1184, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 928, y: 1152, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 736, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 768, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 800, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 832, y: 320, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 1664, y: 576, w: 208, h: 208, layer: 1, sheet: "windmill.json", animation: "pixels_large" }, { x: 1440, y: 768, w: 208, h: 208, layer: 1, sheet: "windmill.json", animation: "pixels_large" }, { x: 1120, y: 608, w: 208, h: 208, layer: 1, sheet: "windmill.json", animation: "pixels_large" }, { x: 736, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 768, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 800, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 832, y: 384, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, ]; ================================================ FILE: src/editor/maps/gentleanim.js ================================================ // Map generated by assettool.js [Sat Sep 30 2023 23:42:21 GMT-0700 (Pacific Daylight Time)] export const tilesetpath = "./tilesets/gentle.png" export const tiledim = 32 export const screenxtiles = 40 export const screenytiles = 32 export const tilesetpxw = 1280 export const tilesetpxh = 1024 export const bgtiles = [ [ [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 241 , 241 , 241 , 241 , 241 , 241 , 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ], [ 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , 241 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], ], [ [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -1 , -1 , -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ],]; export const objmap = [ [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ], [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ],]; export const animatedsprites = [ { x: 576, y: 320, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 608, y: 320, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 544, y: 288, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 608, y: 672, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 576, y: 704, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 608, y: 704, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 576, y: 672, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 480, y: 224, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 480, y: 160, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 480, y: 96, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 608, y: 352, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 544, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 576, y: 480, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 576, y: 512, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 608, y: 512, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 608, y: 544, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 640, y: 704, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 672, y: 672, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 640, y: 640, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 640, y: 672, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 640, y: 736, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 704, y: 704, w: 32, h: 32, layer: 1, sheet: "gentlesparkle.json", animation: "pixels_large" }, { x: 544, y: 352, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 576, y: 352, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 608, y: 384, w: 32, h: 96, layer: 1, sheet: "gentlewaterfall.json", animation: "pixels_large" }, { x: 1248, y: 256, w: 208, h: 208, layer: 1, sheet: "windmill.json", animation: "pixels_large" }, { x: 1728, y: 160, w: 32, h: 32, layer: 1, sheet: "campfire.json", animation: "pixels_large" }, { x: 544, y: 416, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 576, y: 416, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, { x: 608, y: 448, w: 32, h: 64, layer: 2, sheet: "gentlesplash.json", animation: "pixels_large" }, ]; ================================================ FILE: src/editor/maps/mage3.js ================================================ // Map generated by assettool.jsThu Aug 31 2023 00:08:34 GMT-0700 (Pacific Daylight Time) export const tilesetpath = "./tilesets/magecity.png" export const tiledim = 32 export const screenxtiles = 8 export const screenytiles = 44 export const tilesetpxw = 256 export const tilesetpxh = 1408 export const bgtiles = [ [ [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], [ 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], ], [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ 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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], ],]; export const objmap = [ [ [ 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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ 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], [ 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], [ 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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ 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], ], [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -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], [ -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], [ -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -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], [ -1 , -1 , -1 , -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], [ -1 , -1 , -1 , -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -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], [ -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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], ],]; ================================================ FILE: src/editor/maps/serene.js ================================================ // Map generated by assettool.js [Tue Sep 19 2023 09:13:08 GMT-0700 (Pacific Daylight Time)] export const tilesetpath = "./tilesets/Serene.png" export const tiledim = 32 export const screenxtiles = 19 export const screenytiles = 45 export const tilesetpxw = 608 export const tilesetpxh = 1440 export const bgtiles = [ [ [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], [ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , ], ], [ [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -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 , 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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ],]; export const objmap = [ [ [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], [ 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 , ], ], [ [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -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 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], [ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , ], ],]; ================================================ FILE: src/editor/se.html ================================================
level Composite
16 32
Load Tileset
================================================ FILE: src/editor/se.js ================================================ // -- // Simple level editer. // // TODO: // - clear selected_tiles // // Done: // - Delete tiles // - move magic numbers to context / initialization (zIndex, pane size etc.) // - todo fudge factor on g_ctx.tileset // - get rid of dangerous CONFIG.tiledim (use g_ctx.tileDim instead) // - XXX create tilesetpadding for tilesets whos tiles are spaced (e.g. phantasy star II) // - only use fudge to pick sprites rather than fudge and non // - use g_ctx for g_ctx.tileset parameters instead of CONFIG (starting with initTilesetConfig) // - todo print locations on screen // // // Keybindings: // -z - undo // g - overlay 32x32 grid // s - generate .js file to move over to convex/maps/ // m - place a semi-transparent red mask over all tiles. This helps find invisible tiles // d - hold while clicking a tile to delete // p - toggle between 16pixel and 32 pixel. // // Known bugs and annoyances // - if deleting a tile while filter is on, filter isn't refreshed so need to toggle with "m" // -- import * as PIXI from 'pixi.js' import { g_ctx } from './secontext.js' // global context import * as CONFIG from './seconfig.js' import * as UNDO from './undo.js' import * as SPRITEFILE from './spritefile.js' import * as UI from './sehtmlui.js' import { EventSystem } from '@pixi/events'; g_ctx.debug_flag = true; function tileset_index_from_coords(x, y) { let retme = x + (y*g_ctx.tilesettilew); console.log("tileset_index_from_coord ",retme, x, y); return retme; } function level_index_from_coords(x, y) { // place 16px tiles in separate index space let offset = (g_ctx.tiledimx == 16)? CONFIG.MAXTILEINDEX : 0; let retme = x + (y*CONFIG.leveltilewidth) + offset; return retme; } function tileset_index_from_px(x, y) { let coord_x = Math.floor((x - g_ctx.tileset.fudgex) / (g_ctx.tiledimx + CONFIG.tilesetpadding)); let coord_y = Math.floor((y - g_ctx.tileset.fudgey) / (g_ctx.tiledimy+ CONFIG.tilesetpadding)); console.log("tileset_index_from_px ",x, y); return tileset_index_from_coords(coord_x, coord_y); } function level_index_from_px(x, y) { let coord_x = Math.floor(x / g_ctx.tiledimx); let coord_y = Math.floor(y / g_ctx.tiledimy); return level_index_from_coords(coord_x, coord_y); } function tileset_coords_from_index(index) { let x = index % (g_ctx.tilesettilew); let y = Math.floor(index / (g_ctx.tilesettilew)); console.log("tilesettilewidth: ",g_ctx.tilesettilew); console.log("tileset_coords_from_index tile coords: ",index,x,y); return [x,y]; } function tileset_px_from_index(index) { let ret = tileset_coords_from_index(index); return [ret[0] * (g_ctx.tiledimx+CONFIG.tilesetpadding), ret[1] * (g_ctx.tiledimy+CONFIG.tilesetpadding)] ; } // return a sprite of size tileDim given (x,y) starting location function sprite_from_px(x, y) { const bt = PIXI.BaseTexture.from(g_ctx.tilesetpath, { scaleMode: PIXI.SCALE_MODES.NEAREST, }); let w = g_ctx.tiledimx; let h = g_ctx.tiledimy; if(x + w > g_ctx.tilesetpxw) { console.log("sprite_from_px: Warning, texture overrun, truncating"); w = g_ctx.tilesetpxw - x; } if(y + h > g_ctx.tilesetpxh) { console.log("sprite_from_px: Warning, texture overrun, truncating"); y = g_ctx.tilesetpxh - h; } let texture = new PIXI.Texture(bt, new PIXI.Rectangle(x, y, w, h) ); return new PIXI.Sprite(texture); } function DragState() { this.square = new PIXI.Graphics(); this.tooltip = new PIXI.Text('', { fontFamily: 'Courier', fontSize: 12, fill: 0xffffff, align: 'center', }); this.startx = 0; this.starty = 0; this.endx = 0; this.endy = 0; } class LayerContext { constructor(app, pane, num, mod = null) { this.app = app; this.scrollpane = pane; this.num = num; this.widthpx = CONFIG.levelwidth; this.heightpx = CONFIG.levelheight; this.container = new PIXI.Container(); this.sprites = {}; this.composite_sprites = {}; this.dragctx = new DragState(); this.tilearray = Array.from(Array(CONFIG.leveltileheight), () => new Array().fill(null)); app.stage.addChild(this.container); this.mouseshadow = new PIXI.Container(); this.mouseshadow.zIndex = CONFIG.zIndexMouseShadow; this.lasttileindex = -1; this.fudgex = 0; // offset from 0,0 this.fudgey = 0; this.square = new PIXI.Graphics(); this.square.beginFill(0x2980b9); this.square.drawRect(0, 0, CONFIG.levelwidth, CONFIG.levelheight); this.square.endFill(); this.square.eventMode = 'static'; this.container.addChild(this.square); this.square.on('mousemove', onLevelMousemove.bind(this)); this.square.on('mouseover', onLevelMouseover.bind(this)); this.square.on('pointerout', onLevelMouseOut.bind(this)) this.square.on('pointerdown', onLevelPointerDown.bind(null, this)) .on('pointerup', onLevelDragEnd.bind(null, this)) .on('pointerupoutside', onLevelDragEnd.bind(null, this)); if (mod != null && !(mod === g_ctx)) { this.loadFromMapFile(mod); } } loadFromMapFile(mod) { let tiles = []; if (this.num == 0) { tiles = mod.bgtiles[0]; } else if (this.num == 1) { tiles = mod.bgtiles[1]; } else if (this.num == 2) { tiles = mod.objmap[0]; } else if (this.num == 3) { tiles = mod.objmap[1]; } else { console.log("loadFromMapFile: Error unknow layer number"); return; } for (let x = 0; x < tiles.length; x++) { for (let y = 0; y < tiles[0].length; y++) { if (tiles[x][y] != -1) { this.addTileLevelCoords(x, y, mod.DEFAULTILEDIMX, tiles[x][y]); } } } } // this will create a rectangle with an alpha channel for every square that has a sprite. This helps find // sprites that are purely transparent drawFilter() { if (typeof this.filtergraphics == 'undefined') { this.filtertoggle = true; this.filtergraphics = new PIXI.Graphics(); this.filtergraphics.zIndex = CONFIG.zIndexFilter; } if (this.filtertoggle) { this.filtergraphics.beginFill(0xff0000, 0.3); for (let i in this.sprites) { let spr = this.sprites[i]; this.filtergraphics.drawRect(spr.x, spr.y, g_ctx.tiledimx, g_ctx.tiledimy); } this.filtergraphics.endFill(); this.container.addChild(this.filtergraphics); }else{ this.filtergraphics.clear(); this.container.removeChild(this.filtergraphics); } this.filtertoggle = ! this.filtertoggle; } // add tile of "index" to Level at location x,y addTileLevelCoords(x, y, dim, index) { return this.addTileLevelPx(x * dim, y * dim, index); } // -- delete all sprites / textures on a given index // will NOOP if no tile exists deleteFromIndex(index) { if(g_ctx.debug_flag){ console.log("deleteFromIndex ",index) } if(this.sprites.hasOwnProperty(index)){ let ctile = this.sprites[index]; let row = Math.floor(ctile.y / g_ctx.tiledimy); let col = Math.floor(ctile.x / g_ctx.tiledimx); for(let x = 0; x < this.tilearray[row].length; x++){ if(this.tilearray[row][x].x == col * g_ctx.tiledimx){ console.log("Removing texture from tilearray ",x,row); this.tilearray[row].splice(x, 1); } } this.container.removeChild(this.sprites[index]); delete this.sprites[index]; this.updateAnimatedTiles(); } } // add tile of "index" to Level at location x,y addTileLevelPx(x, y, index) { let xPx = x; let yPx = y; let ctile = null; let ctile2 = null; let pxloc = tileset_px_from_index(index); ctile = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); ctile.index = index; // snap to grid const dx = g_ctx.tiledimx; const dy = g_ctx.tiledimy; ctile.x = Math.floor(xPx / dx) * dx; ctile.y = Math.floor(yPx / dy) * dy; // Stuff tileset coords into ctile for writing to ts file ctile.tspx = [pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey]; let new_index = level_index_from_px(ctile.x, ctile.y); if (g_ctx.debug_flag) { console.log('addTileLevelPx ', this.num, ' ctile.x ', ctile.x, 'ctile.y ', ctile.y, "index ", index, "new_index", new_index); } if (!g_ctx.dkey) { this.container.addChild(ctile); } if (this.sprites.hasOwnProperty(new_index)) { this.deleteFromIndex(new_index); } if (!g_ctx.dkey) { this.tilearray[Math.floor(yPx / dy)].push(ctile); this.sprites[new_index] = ctile; } else if (typeof this.filtergraphics != 'undefined') { this.filtergraphics.clear(); this.drawFilter(); this.drawFilter(); // do twice to get toggle back to original state } return new_index; } // -- // FIXME : currently just a naive loop. // -- updateAnimatedTiles() { console.log("updateAnimatedTiles"); for (let row = 0; row < CONFIG.leveltileheight; row++) { if (!this.tilearray[row][0]) { continue; } let textures = []; for (let x = 0; x < this.tilearray[row].length; x++) { textures.push(this.tilearray[row][x].texture); } const as = new PIXI.AnimatedSprite(textures); as.x = row * g_ctx.tiledimx; as.y = this.num * g_ctx.tiledimy; as.animationSpeed = .1; as.autoUpdate = true; as.play(); if (this.tilearray[row].hasOwnProperty('as')){ g_ctx.composite.container.removeChild(this.tilearray[row].as); } this.tilearray[row].as = as; g_ctx.composite.container.addChild(as); } } } // class LayerContext class TilesetContext { constructor(app, mod = g_ctx) { this.app = app; this.container = new PIXI.Container(); console.log(mod.tilesetpath); const texture = PIXI.Texture.from(mod.tilesetpath); const bg = new PIXI.Sprite(texture); this.widthpx = g_ctx.tilesetpxw; this.heightpx = g_ctx.tilesetpxh; this.square = new PIXI.Graphics(); this.square.beginFill(0x2980b9); this.square.drawRect(0, 0, mod.tilesetpxw, mod.tilesetpxh); this.square.endFill(); this.square.eventMode = 'static'; this.container.addChild(this.square); this.container.addChild(bg); this.app.stage.addChild(this.container); this.fudgex = 0; // offset from 0,0 this.fudgey = 0; this.dragctx = new DragState(); this.square.on('mousedown', function (e) { g_ctx.tile_index = tileset_index_from_px(e.global.x, e.global.y); if(g_ctx.debug_flag) { console.log("g_ctx.tileset mouse down. index "+g_ctx.tile_index); } }); this.square.on('pointerdown', onTilesetDragStart) .on('pointerup', onTilesetDragEnd) .on('pointerupoutside', onTilesetDragEnd); } } // class TilesetContext class CompositeContext { constructor(app) { this.app = app; this.widthpx = CONFIG.levelwidth; this.heightpx = CONFIG.levelheight; this.container = new PIXI.Container(); this.container.sortableChildren = true; this.app.stage.addChild(this.container); this.sprites = {}; this.circle = new PIXI.Graphics(); this.circle.zIndex = CONFIG.zIndexCompositePointer; this.fudgex = 0; // offset from 0,0 this.fudgey = 0; this.mouseshadow = new PIXI.Container(); this.mouseshadow.zIndex = CONFIG.zIndexMouseShadow; this.lasttileindex = -1; this.square = new PIXI.Graphics(); this.square.beginFill(0x2980b9); this.square.drawRect(0, 0, CONFIG.levelwidth, CONFIG.levelheight); this.square.endFill(); this.square.eventMode = 'static'; this.container.addChild(this.square); this.square.on('mousedown', onCompositeMousedown.bind(null, this)); } } // class CompositeContext function doimport (str) { if (globalThis.URL.createObjectURL) { const blob = new Blob([str], { type: 'text/javascript' }) const url = URL.createObjectURL(blob) const module = import(url) URL.revokeObjectURL(url) // GC objectURLs return module } const url = "data:text/javascript;base64," + btoa(moduleData) return import(url) } function resetPanes() { g_ctx.tiledimx = 16; g_ctx.tiledimy = 16; g_ctx.composite.container.removeChildren(); g_ctx.composite = new CompositeContext(g_ctx.composite_app); g_ctx.tileset_app.stage.removeChildren() g_ctx.tileset = new TilesetContext(g_ctx.tileset_app); g_ctx.g_layer_apps[0].stage.removeChildren() g_ctx.g_layers[0] = new LayerContext(g_ctx.g_layer_apps[0], document.getElementById("layer0pane"), 0); g_ctx.g_layer_apps[1].stage.removeChildren() g_ctx.g_layers[1] = new LayerContext(g_ctx.g_layer_apps[1], document.getElementById("layer1pane"), 1); g_ctx.g_layer_apps[2].stage.removeChildren() g_ctx.g_layers[2] = new LayerContext(g_ctx.g_layer_apps[2], document.getElementById("layer2pane"), 2); g_ctx.g_layer_apps[3].stage.removeChildren() g_ctx.g_layers[3] = new LayerContext(g_ctx.g_layer_apps[3], document.getElementById("layer3pane"), 3); redrawGrid(); } function downloadpng(filename) { let newcontainer = new PIXI.Container(); let children = [...g_ctx.composite.container.children]; for(let i = 0; i < children.length; i++) { let child = children[i]; if (! child.hasOwnProperty('isSprite') || !child.isSprite){ console.log(child); continue; } // console.log(child, typeof child); g_ctx.composite.container.removeChild(child); newcontainer.addChild(child); } renderer.plugins.extract.canvas(newcontainer).toBlob(function (b) { //renderer.plugins.extract.canvas(g_ctx.composite.container).toBlob(function (b) { console.log(b); var a = document.createElement("a"); document.body.append(a); a.download = filename; a.href = URL.createObjectURL(b); a.click(); a.remove(); }, "image/png"); } window.saveCompositeAsImage = () => { downloadpng("g_ctx.composite.png"); } window.onTab = (evt, tabName) => { // Declare all variables var i, tabcontent, tablinks; // Get all elements with class="tabcontent" and hide them tabcontent = document.getElementsByClassName("tabcontent"); for (i = 0; i < tabcontent.length; i++) { tabcontent[i].style.display = "none"; } // Get all elements with class="tablinks" and remove the class "active" tablinks = document.getElementsByClassName("tablinks"); for (i = 0; i < tablinks.length; i++) { tablinks[i].className = tablinks[i].className.replace(" active", ""); } // Show the current tab, and add an "active" class to the button that opened the tab document.getElementById(tabName).style.display = "block"; evt.currentTarget.className += " active"; if (tabName == "map"){ map_app.stage.addChild(g_ctx.composite.container); }else { g_ctx.composite.app.stage.addChild(g_ctx.composite.container); } } window.addEventListener( "keyup", (event) => { if (event.code == "KeyD"){ g_ctx.dkey = false; g_ctx.g_layers.map( (l) => l.container.addChild(l.mouseshadow)); g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow); } }); window.addEventListener( "keydown", (event) => { if (event.code == "KeyD"){ g_ctx.dkey = true; g_ctx.g_layers.map((l) => l.container.removeChild(l.mouseshadow) ); g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow); } if (event.code == 'KeyS'){ SPRITEFILE.generate_sprite_file(); } else if (event.code == 'Escape'){ g_ctx.selected_tiles = []; g_ctx.g_layers.map((l) => l.mouseshadow.removeChildren()); composite.mouseshadow.removeChildren(); } else if (event.code == 'KeyM'){ g_ctx.g_layers.map((l) => l.drawFilter () ); }else if (event.code == 'KeyP'){ setGridSize((g_ctx.tiledimx == 16)?32:16); } else if (event.ctrlKey && event.code === 'KeyZ'){ let undome = UNDO.undo_pop(); if (!undome) { return; } let layer = undome.shift(); for(let i = 0; i < undome.length; i++) { if (g_ctx.debug_flag) { console.log("Undo removing ", undome[i]) } layer.container.removeChild(layer.sprites[undome[i]]); g_ctx.composite.container.removeChild(layer.composite_sprites[undome[i]]); } } else if (event.shiftKey && event.code == 'ArrowUp') { g_ctx.tileset.fudgey -= 1; redrawGridPane(g_ctx.tileset); } else if (event.shiftKey && event.code == 'ArrowDown') { g_ctx.tileset.fudgey += 1; redrawGridPane(g_ctx.tileset); } else if (event.shiftKey && event.code == 'ArrowLeft') { g_ctx.tileset.fudgex -= 1; redrawGridPane(g_ctx.tileset); } else if (event.shiftKey && event.code == 'ArrowRight') { g_ctx.tileset.fudgex += 1; redrawGridPane(g_ctx.tileset); } } ); // Listen to pointermove on stage once handle is pressed. function onTilesetDragStart(e) { if (g_ctx.debug_flag) { console.log("onDragStartTileset()"); } g_ctx.tileset.app.stage.eventMode = 'static'; g_ctx.tileset.app.stage.addEventListener('pointermove', onTilesetDrag); g_ctx.tileset.dragctx.startx = e.global.x; g_ctx.tileset.dragctx.starty = e.global.y; g_ctx.tileset.dragctx.endx = e.global.x; g_ctx.tileset.dragctx.endy = e.global.y; g_ctx.tileset.app.stage.addChild(g_ctx.tileset.dragctx.square); // g_ctx.tileset.app.stage.addChild(g_ctx.tileset.dragctx.tooltip); g_ctx.selected_tiles = []; } // Stop dragging feedback once the handle is released. function onTilesetDragEnd(e) { if (g_ctx.debug_flag) { console.log("onDragEndTileset()"); } g_ctx.tileset.app.stage.eventMode = 'auto'; g_ctx.tileset.app.stage.removeEventListener('pointermove', onTilesetDrag); g_ctx.tileset.app.stage.removeChild(g_ctx.tileset.dragctx.square); g_ctx.tileset.app.stage.removeChild(g_ctx.tileset.dragctx.tooltip); let starttilex = Math.floor((g_ctx.tileset.dragctx.startx - g_ctx.tileset.fudgex) / g_ctx.tiledimx); let starttiley = Math.floor((g_ctx.tileset.dragctx.starty - g_ctx.tileset.fudgey) / g_ctx.tiledimy); let endtilex = Math.floor((g_ctx.tileset.dragctx.endx - g_ctx.tileset.fudgex) / g_ctx.tiledimx); let endtiley = Math.floor((g_ctx.tileset.dragctx.endy - g_ctx.tileset.fudgey) / g_ctx.tiledimy); if (g_ctx.debug_flag) { console.log("sx sy ex ey ", starttilex, ",", starttiley, ",", endtilex, ",", endtiley); } // let mouse clicked handle if there isn't a multiple tile square if(starttilex === endtilex && starttiley === endtiley ){ return; } g_ctx.tile_index = tileset_index_from_px(e.global.x, e.global.y); let origx = starttilex; let origy = starttiley; for(let y = starttiley; y <= endtiley; y++){ for(let x = starttilex; x <= endtilex; x++){ let squareindex = (y * g_ctx.tilesettilew) + x; console.log("Pushing into st ", x - origx,y - origy, squareindex); g_ctx.selected_tiles.push([x - origx,y - origy,squareindex]); } } // if we're still 16x16 assume we're resizing the grid if (g_ctx.tiledimx == 16 && g_ctx.tiledimy == 16) { // for sprite tool, we want to set the tilesize based on the selected region // FIXME .. don't use magic number 16 g_ctx.tiledimx = (1 + endtilex - starttilex) * 16; g_ctx.tiledimy = (1 + endtiley - starttiley) * 16; redrawGrid(); g_ctx.selected_tiles = []; 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); } g_ctx.tileset.dragctx.square.clear(); // g_ctx.tileset.dragctx.tooltip.clear(); } function onTilesetDrag(e) { if (g_ctx.debug_flag) { console.log("onDragTileset()"); } g_ctx.tileset.dragctx.endx = e.global.x; g_ctx.tileset.dragctx.endy = e.global.y; g_ctx.tileset.dragctx.square.clear(); g_ctx.tileset.dragctx.square.beginFill(0xFF3300, 0.3); g_ctx.tileset.dragctx.square.lineStyle(2, 0xffd900, 1); g_ctx.tileset.dragctx.square.moveTo(g_ctx.tileset.dragctx.startx, g_ctx.tileset.dragctx.starty); g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.endx, g_ctx.tileset.dragctx.starty); g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.endx, g_ctx.tileset.dragctx.endy); g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.startx, g_ctx.tileset.dragctx.endy); g_ctx.tileset.dragctx.square.closePath(); g_ctx.tileset.dragctx.square.endFill(); // g_ctx.tileset.dragctx.tooltip.clear(); // g_ctx.tileset.dragctx.tooltip.beginFill(0xFF3300, 0.3); // g_ctx.tileset.dragctx.tooltip.lineStyle(2, 0xffd900, 1); // g_ctx.tileset.dragctx.tooltip.drawRect(e.global.x, e.global.y, 20,8); // g_ctx.tileset.dragctx.tooltip.endFill(); } //g_ctx.tileset.app.stage.addChild(g_ctx.tileset.container); function redrawGridPane(pane) { if (typeof pane.gridgraphics != 'undefined') { pane.container.removeChild(pane.gridgraphics); } pane.gridgraphics = new PIXI.Graphics(); let gridsizex = g_ctx.tiledimx; let gridsizey = g_ctx.tiledimy; pane.gridgraphics.lineStyle(1, 0x000000, 1); let index = 0; for (let i = 0; i < pane.widthpx; i += gridsizex) { pane.gridgraphics.moveTo(i + pane.fudgex, 0 + pane.fudgey); pane.gridgraphics.lineTo(i + pane.fudgex, pane.heightpx + pane.fudgey); pane.gridgraphics.moveTo(i + gridsizex + pane.fudgex, 0 + pane.fudgey); pane.gridgraphics.lineTo(i + gridsizex + pane.fudgex, pane.heightpx + pane.fudgey); } for (let j = 0; j < pane.heightpx; j += gridsizey) { pane.gridgraphics.moveTo(0 + pane.fudgex, j + gridsizey + pane.fudgey); pane.gridgraphics.lineTo(pane.widthpx + pane.fudgex, j + gridsizey + pane.fudgey); pane.gridgraphics.moveTo(0 + pane.fudgex, j + pane.fudgey); pane.gridgraphics.lineTo(pane.heightpx + pane.fudgex, j + pane.fudgey); } if (pane.gridvisible) { pane.container.addChild(pane.gridgraphics); } pane.container.addChild(pane.gridgraphics); pane.gridvisible = true; } function redrawGrid() { g_ctx.g_layers.map((l) => redrawGridPane(l)); redrawGridPane(g_ctx.tileset); redrawGridPane(g_ctx.composite); } // -- // Variable placement logic Level1 // -- function centerCompositePane(x, y){ var compositepane = document.getElementById("compositepane"); compositepane.scrollLeft = x - (CONFIG.htmlCompositePaneW/2); compositepane.scrollTop = y - (CONFIG.htmlCompositePaneH/2); } function centerLayerPanes(x, y){ // TODO remove magic number pulled from index.html g_ctx.g_layers.map((l) => { l.scrollpane.scrollLeft = x - (CONFIG.htmlCompositePaneW/2); l.scrollpane.scrollTop = y - (CONFIG.htmlCompositePaneH/2); }); } function onLevelMouseover(e) { let x = e.data.global.x; let y = e.data.global.y; if(g_ctx.debug_flag){ console.log("onLevelMouseOver ",this.num); } if (x < this.scrollpane.scrollLeft || x > this.scrollpane.scrollLeft + CONFIG.htmlCompositePaneW) { return; } if (y < this.scrollpane.scrollTop || y > this.scrollpane.scrollTop + CONFIG.htmlCompositePaneH) { return; } if (this.lasttileindex != g_ctx.tile_index) { this.mouseshadow.removeChildren(0); g_ctx.composite.mouseshadow.removeChildren(0); if (g_ctx.selected_tiles.length == 0) { let shadowsprite = null; let shadowsprite2 = null; let pxloc = tileset_px_from_index(g_ctx.tile_index); shadowsprite = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); shadowsprite2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); shadowsprite.alpha = .5; shadowsprite2.alpha = .5; this.mouseshadow.addChild(shadowsprite); g_ctx.composite.mouseshadow.addChild(shadowsprite2); } else { // TODO! adjust for fudge for (let i = 0; i < g_ctx.selected_tiles.length; i++) { let tile = g_ctx.selected_tiles[i]; console.log("TILE", tile); let pxloc = tileset_px_from_index(tile[2]); console.log('PXLOC',pxloc); const shadowsprite = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); const shadowsprite2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey); shadowsprite.x = tile[0] * g_ctx.tiledimx; shadowsprite.y = tile[1] * g_ctx.tiledimy; shadowsprite2.x = tile[0] * g_ctx.tiledimx; shadowsprite2.y = tile[1] * g_ctx.tiledimy; shadowsprite.alpha = .5; shadowsprite2.alpha = .5; this.mouseshadow.addChild(shadowsprite); g_ctx.composite.mouseshadow.addChild(shadowsprite2); } } this.mouseshadow.x = x - 16; this.mouseshadow.y = y - 16; this.container.removeChild(this.mouseshadow); g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow); this.container.addChild(this.mouseshadow); g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow); } g_ctx.composite.app.stage.removeChild(g_ctx.composite.circle); g_ctx.composite.app.stage.addChild(g_ctx.composite.circle); } function onLevelMouseOut(e) { if (g_ctx.debug_flag) { console.log("onLevelMouseOut ",this.num); } this.mouseshadow.removeChildren(0); g_ctx.composite.mouseshadow.removeChildren(0); } function onLevelMousemove(e) { let x = e.data.global.x; let y = e.data.global.y; // FIXME TEST CODE this.mouseshadow.x = x-8; this.mouseshadow.y = y-8; g_ctx.composite.mouseshadow.x = x-8; g_ctx.composite.mouseshadow.y = y-8; // FIXME TEST CODE if (x < this.scrollpane.scrollLeft || x > this.scrollpane.scrollLeft + CONFIG.htmlCompositePaneW) { return; } if (y < this.scrollpane.scrollTop || y > this.scrollpane.scrollTop + CONFIG.htmlCompositePaneH) { return; } g_ctx.composite.circle.clear(); g_ctx.composite.circle.beginFill(0xe50000, 0.5); g_ctx.composite.circle.drawCircle(e.data.global.x, e.data.global.y, 3); g_ctx.composite.circle.endFill(); } function onCompositeMousedown(layer, e) { if (g_ctx.debug_flag) { console.log('onCompositeMouseDown: X', e.data.global.x, 'Y', e.data.global.y); } let xorig = e.data.global.x; let yorig = e.data.global.y; centerLayerPanes(xorig,yorig); } // Place with no variable target at destination function levelPlaceNoVariable(layer, e) { if (g_ctx.debug_flag) { console.log('levelPlaceNoVariable: X', e.data.global.x, 'Y', e.data.global.y); } let xorig = e.global.x; let yorig = e.global.y; // No need to center pane // centerCompositePane(xorig,yorig); if (g_ctx.dkey || g_ctx.selected_tiles.length == 0) { let ti = layer.addTileLevelPx(e.global.x, e.global.y, g_ctx.tile_index); UNDO.undo_add_single_index_as_task(layer, ti); } else { let undolist = []; UNDO.undo_mark_task_start(layer); for (let index of g_ctx.selected_tiles) { let ti = layer.addTileLevelPx(xorig + index[0] * g_ctx.tiledimx, yorig + index[1] * g_ctx.tiledimy, index[2]); UNDO.undo_add_index_to_task(ti); } UNDO.undo_mark_task_end(); } layer.updateAnimatedTiles(); } // Listen to pointermove on stage once handle is pressed. function onLevelPointerDown(layer, e) { if (g_ctx.debug_flag) { console.log("onLevelPointerDown()"); } layer.app.stage.eventMode = 'static'; layer.app.stage.addEventListener('pointermove', onLevelDrag.bind(null, layer, e)); layer.container.removeChild(layer.mouseshadow); g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow); layer.dragctx.startx = e.data.global.x; layer.dragctx.starty = e.data.global.y; layer.dragctx.endx = e.data.global.x; layer.dragctx.endy = e.data.global.y; layer.app.stage.addChild(layer.dragctx.square); layer.app.stage.addChild(layer.dragctx.tooltip); } function onLevelDrag(layer, e) { if(layer.dragctx.startx == -1){ layer.dragctx.square.clear(); return; } layer.dragctx.endx = e.global.x; layer.dragctx.endy = e.global.y; if (g_ctx.debug_flag) { console.log("onLevelDrag()"); } layer.dragctx.square.clear(); layer.dragctx.square.beginFill(0xFF3300, 0.3); layer.dragctx.square.lineStyle(2, 0xffd900, 1); layer.dragctx.square.moveTo(layer.dragctx.startx, layer.dragctx.starty); layer.dragctx.square.lineTo(layer.dragctx.endx, layer.dragctx.starty); layer.dragctx.square.lineTo(layer.dragctx.endx, layer.dragctx.endy); layer.dragctx.square.lineTo(layer.dragctx.startx, layer.dragctx.endy); layer.dragctx.square.closePath(); layer.dragctx.square.endFill(); const vwidth = Math.floor((layer.dragctx.endx - layer.dragctx.startx)/g_ctx.tiledimx); const vheight = Math.floor((layer.dragctx.endy - layer.dragctx.starty)/g_ctx.tiledimy); layer.dragctx.tooltip.x = e.global.x + 16; layer.dragctx.tooltip.y = e.global.y - 4; layer.dragctx.tooltip.text = "["+vwidth+","+vheight+"]\n"+ "("+Math.floor(e.global.x/g_ctx.tiledimx)+","+Math.floor(e.global.y/g_ctx.tiledimy)+")"; //layer.dragctx.tooltip.text = "("+e.global.x+","+e.global.y+")"; } function onLevelCreateAnimatedSprite(row) { } // Stop dragging feedback once the handle is released. function onLevelDragEnd(layer, e) { layer.dragctx.endx = e.data.global.x; layer.dragctx.endy = e.data.global.y; if(layer.dragctx.startx == -1){ console.log("onLevelDragEnd() start is -1 bailing"); return; } if (g_ctx.debug_flag) { console.log("onLevelDragEnd()"); } layer.container.addChild(layer.mouseshadow); g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow); layer.app.stage.eventMode = 'auto'; layer.app.stage.removeChild(layer.dragctx.square); layer.app.stage.removeChild(layer.dragctx.tooltip); let starttilex = Math.floor(layer.dragctx.startx / g_ctx.tiledimx); let starttiley = Math.floor(layer.dragctx.starty / g_ctx.tiledimy); let endtilex = Math.floor(layer.dragctx.endx / g_ctx.tiledimx); let endtiley = Math.floor(layer.dragctx.endy / g_ctx.tiledimy); if (g_ctx.debug_flag) { console.log("sx ", starttilex, " ex ", endtilex); console.log("sy ", starttiley, " ey ", endtiley); } // no variable placement. if(starttilex === endtilex && starttiley == endtiley ){ levelPlaceNoVariable(layer, e); layer.dragctx.startx = -1; layer.dragctx.endx = -1; layer.dragctx.starty = -1; layer.dragctx.endy = -1; return; } if (g_ctx.selected_tiles.length == 0) { UNDO.undo_mark_task_start(layer); for (let i = starttilex; i <= endtilex; i++) { for (let j = starttiley; j <= endtiley; j++) { let squareindex = (j * g_ctx.tilesettilew) + i; let ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, g_ctx.tile_index); UNDO.undo_add_index_to_task(ti); } } UNDO.undo_mark_task_end(); } else { // figure out selected grid let selected_grid = Array.from(Array(64), () => new Array(64)); // FIXME ... hope 64x64 is enough let row = 0; let column = 0; let selected_row = g_ctx.selected_tiles[0][1]; // selected_grid[0] = []; for (let index of g_ctx.selected_tiles) { // console.log("Selected row ", selected_row, index); if(index[1] != selected_row){ selected_row = index[1]; row++; column = 0; //selected_grid[row] = []; } selected_grid[column++][row] = index; } // at this point should have a 3D array of the selected tiles and the size should be row, column UNDO.undo_mark_task_start(layer); let ti=0; for (let i = starttilex; i <= endtilex; i++) { for (let j = starttiley; j <= endtiley; j++) { let squareindex = (j * g_ctx.tilesettilew) + i; if (j === starttiley) { // first row if (i === starttilex) { // top left corner ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[0][0][2]); } else if (i == endtilex) { // top right corner ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[column - 1][0][2]); } else { // top middle ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[1][0][2]); } } else if (j === endtiley) { // last row if (i === starttilex) { // bottom left corner ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[0][row][2]); } else if (i == endtilex) { // bottom right corner ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[column - 1][row][2]); } else { // bottom middle ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[1][row][2]); } } else { // middle row if (i === starttilex) { // middle left ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[0][(row > 0)? 1 : 0][2]); } else if (i === endtilex) { // middle end ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[column - 1][(row > 0)? 1 : 0][2]); } else { // middle middle ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimy, selected_grid[1][(row > 0)? 1 : 0][2]); } } UNDO.undo_add_index_to_task(ti); } } UNDO.undo_mark_task_end(); } layer.dragctx.square.clear(); layer.dragctx.startx = -1; layer.dragctx.starty = -1; } // -- // Initialized all pixi apps / components for application // -- function initPixiApps() { // -- Editor wide globals -- // First layer of level const level_app0 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level0') }); let layer0 = new LayerContext(level_app0, document.getElementById("layer0pane"), 0); // second layer of level const level_app1 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level1') }); let layer1 = new LayerContext(level_app1, document.getElementById("layer1pane"), 1); // object layer of level const level_app2 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level3') }); let layer2 = new LayerContext(level_app2, document.getElementById("layer2pane"), 2); // object layer of level const level_app3 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level4') }); let layer3 = new LayerContext(level_app3, document.getElementById("layer3pane"), 3); g_ctx.g_layer_apps = []; g_ctx.g_layer_apps.push(level_app0 ); g_ctx.g_layer_apps.push(level_app1); g_ctx.g_layer_apps.push(level_app2); g_ctx.g_layer_apps.push(level_app3); g_ctx.g_layers = []; g_ctx.g_layers.push(layer0); g_ctx.g_layers.push(layer1); g_ctx.g_layers.push(layer2); g_ctx.g_layers.push(layer3); // g_ctx.composite view g_ctx.composite_app = new PIXI.Application({ backgroundAlpha: 0, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('composite') }); g_ctx.composite = new CompositeContext(g_ctx.composite_app); // map tab g_ctx.map_app = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('mapcanvas') }); // g_ctx.tileset // g_ctx.tileset_app = new PIXI.Application({ width: g_ctx.tilesetpxw, height: g_ctx.tilesetpxh, view: document.getElementById('tileset') }); g_ctx.tileset_app = new PIXI.Application({ width: 5632 , height: 8672, view: document.getElementById('tileset') }); //g_ctx.tileset_app = new PIXI.Application({ backgroundColor: 0x2980b9, width: 5632 , height: 8672, view: document.getElementById('tileset') }); const { renderer } = g_ctx.tileset_app; // Install the EventSystem renderer.addSystem(EventSystem, 'tileevents'); g_ctx.tileset = new TilesetContext(g_ctx.tileset_app); } // -- // Initialize handlers for file loading // -- function initLevelLoader() { let filecontent = ""; const fileInput = document.getElementById('levelfile'); fileInput.onchange = (evt) => { if (!window.FileReader) return; // Browser is not compatible var reader = new FileReader(); reader.onload = function (evt) { if (evt.target.readyState != 2) return; if (evt.target.error) { alert('Error while reading file'); return; } filecontent = evt.target.result; doimport(filecontent).then(mod => loadMapFromModule(mod)); }; reader.readAsText(evt.target.files[0]); } } function setGridSize(size) { if (size == 16) { if (g_ctx.tiledimx == 16) { return; } g_ctx.tilesettilew = (g_ctx.tilesettilew/ (size / g_ctx.tiledimx)); g_ctx.tilesettileh = (g_ctx.tilesettileh / (size / g_ctx.tiledimy)); g_ctx.tiledimx = 16; g_ctx.dimlog = Math.log2(g_ctx.tiledimx); g_ctx.curtiles = g_ctx.tiles16; console.log("set to curTiles16"); } else if (size == 32) { if (g_ctx.tiledimx == 32) { return; } g_ctx.tilesettilew = (g_ctx.tilesettilew/ (size / g_ctx.tiledimx)); g_ctx.tilesettileh = (g_ctx.tilesettileh / (size / g_ctx.tiledimy)); g_ctx.tiledimx = 32; g_ctx.dimlog = Math.log2(g_ctx.tiledimx); g_ctx.curtiles = g_ctx.tiles32; console.log("set to curTiles32"); } else { console.debug("Invalid TileDim!"); return; } redrawGrid(); } function initRadios() { var rad = document.myForm.radioTiledim; var prev = null; for (var i = 0; i < rad.length; i++) { rad[i].addEventListener('change', function () { if (this !== prev) { prev = this; } setGridSize(this.value); }); } } // -- // Load in default tileset and use to set properties // -- const initTilesConfig = async (path = CONFIG.DEFAULTTILESETPATH) => { g_ctx.tilesetpath = path; return new Promise((resolve, reject) => { const texture = new PIXI.BaseTexture(g_ctx.tilesetpath); console.log("Loading texture ",g_ctx.tilesetpath); texture .on('loaded', function() { // size of g_ctx.tileset in px g_ctx.tilesetpxw = texture.width; g_ctx.tilesetpxh = texture.height; console.log("Texture size w:", g_ctx.tilesetpxw, "h:", g_ctx.tilesetpxh); // size of g_ctx.tileset in tiles let tileandpad = g_ctx.tiledimx + CONFIG.tilesetpadding; let numtilesandpadw = Math.floor(g_ctx.tilesetpxw / tileandpad); g_ctx.tilesettilew = numtilesandpadw + Math.floor((g_ctx.tilesetpxw - (numtilesandpadw * tileandpad))/g_ctx.tiledimx); let numtilesandpadh = Math.floor(g_ctx.tilesetpxh / tileandpad); g_ctx.tilesettileh = numtilesandpadh + Math.floor((g_ctx.tilesetpxh - (numtilesandpadh * tileandpad))/g_ctx.tiledimy); console.log("Number of x tiles ",g_ctx.tilesettilew," y tiles ",g_ctx.tilesettileh); g_ctx.MAXTILEINDEX = g_ctx.tilesettilew * g_ctx.tilesettileh; texture.destroy(); resolve(); }); }); }; function initTiles() { // load g_ctx.tileset into a global array of textures for blitting onto levels const bt = PIXI.BaseTexture.from(g_ctx.tilesetpath, { scaleMode: PIXI.SCALE_MODES.NEAREST, }); for (let x = 0; x < CONFIG.tilesettilewidth; x++) { for (let y = 0; y < CONFIG.tilesettileheight; y++) { g_ctx.tiles32[x + y * CONFIG.tilesettilewidth] = new PIXI.Texture( bt, new PIXI.Rectangle(x * 32, y * 32, 32, 32), ); } } for (let x = 0; x < CONFIG.tilesettilewidth * 2; x++) { for (let y = 0; y < CONFIG.tilesettileheight * 2; y++) { g_ctx.tiles16[x + y * CONFIG.tilesettilewidth * 2] = new PIXI.Texture( bt, new PIXI.Rectangle(x * 16, y * 16, 16, 16), ); } } g_ctx.curtiles = g_ctx.tiles32; } function newTilesetFromFile(){ initTilesConfig(g_ctx.tilesetpath).then(resetPanes); } async function init() { UI.initMainHTMLWindow(); await initTilesConfig(); // needs to be called before Pixi apps are initialized initPixiApps(); initRadios(); initTiles(); initLevelLoader(); UI.initCompositePNGLoader(); UI.initTilesetLoader(newTilesetFromFile); redrawGrid(); } init(); ================================================ FILE: src/editor/seconfig.js ================================================ //export const DEFAULTTILESETPATH = "./spritesheets/women.png"; //export const DEFAULTILEDIMX = 32; // px //export const DEFAULTILEDIMY = 34; // px //export const DEFAULTTILESETPATH = "./spritesheets/doll.png"; //export const DEFAULTILEDIMX = 48; // px //export const DEFAULTILEDIMY = 48; // px // export const DEFAULTTILESETPATH = "./spritesheets/peeps.png"; // export const DEFAULTILEDIMX = 48; // px // export const DEFAULTILEDIMY = 96; // px export const DEFAULTTILESETPATH = "./spritesheets/tall.png"; //export const DEFAULTTILESETPATH = "./spritesheets/Clothes_Hanging_1_32x32.png" export const DEFAULTILEDIMX = 16; // px export const DEFAULTILEDIMY = 16; // px // export const DEFAULTTILESETPATH = "./spritesheets/wateranimate2.png"; // export const DEFAULTILEDIMX = 32; // px // export const DEFAULTILEDIMY = 32; // px // If there is padding between tilesets, set this to the pixel size export const tilesetpadding = 0; // width / height of layer panes export const levelwidth = 2048; // px export const levelheight = 1536; // px export let leveltilewidth = Math.floor(levelwidth / DEFAULTILEDIMX); export let leveltileheight = Math.floor(levelheight / DEFAULTILEDIMX); export const MAXTILEINDEX = leveltilewidth * leveltileheight; // -- HTML export const htmlLayerPaneW = 800; export const htmlLayerPaneH = 600; export const htmlTilesetPaneW = 800; export const htmlTilesetPaneH = 600; export const htmlCompositePaneW = 800; export const htmlCompositePaneH = 600; // -- zIndex // 1-10 taken by layers export const zIndexFilter = 20; export const zIndexMouseShadow = 30; export const zIndexGrid = 50; export const zIndexCompositePointer = 100; ================================================ FILE: src/editor/secontext.js ================================================ import * as PIXI from 'pixi.js' import * as CONFIG from './seconfig.js' var ContextCreate = (function(){ function ContextSingleton() { this.tilesetpxw = 0; this.tilesetpxh = 0; this.tilesettilew = 0; this.tilesettileh = 0; this.MAXTILEINDEX = 0; this.tile_index = 0; this.selected_tiles = []; // current set of selected tiles this.tiledimx = CONFIG.DEFAULTILEDIMX ; // px this.tiledimy = CONFIG.DEFAULTILEDIMY; // px this.dimlog = Math.log2(this.tileDim); //log2(TileDim) this.dkey = false; // is 'd' key depressed? (for delete) this.tiles32 = []; // all tiles from tilemap (32x32) this.tiles16 = []; this.fudgetiles = []; this.g_layers = []; // level layers } var instance; return { getInstance: function(){ if (instance == null) { instance = new ContextSingleton(); // Hide the constructor so the returned object can't be new'd... instance.constructor = null; } return instance; } }; })(); // global shared state between all panes export let g_ctx = ContextCreate.getInstance(); ================================================ FILE: src/editor/sehtmlui.js ================================================ import * as PIXI from 'pixi.js' import { g_ctx } from './secontext.js' // global context import * as CONFIG from './seconfig.js' // -- // Set sizes and limits for HTML in main UI // -- export function initMainHTMLWindow() { document.getElementById("layer0pane").style.maxWidth = ""+CONFIG.htmlLayerPaneW+"px"; document.getElementById("layer0pane").style.maxHeight = ""+CONFIG.htmlLayerPaneH+"px"; document.getElementById("layer1pane").style.maxWidth = ""+CONFIG.htmlLayerPaneW+"px"; document.getElementById("layer1pane").style.maxHeight = ""+CONFIG.htmlLayerPaneH+"px"; document.getElementById("layer2pane").style.maxWidth = ""+CONFIG.htmlLayerPaneW+"px"; document.getElementById("layer2pane").style.maxHeight = ""+CONFIG.htmlLayerPaneH+"px"; document.getElementById("layer3pane").style.maxWidth = ""+CONFIG.htmlLayerPaneW+"px"; document.getElementById("layer3pane").style.maxHeight = ""+CONFIG.htmlLayerPaneH+"px"; document.getElementById("tilesetpane").style.maxWidth = ""+CONFIG.htmlTilesetPaneW+"px"; document.getElementById("tilesetpane").style.maxHeight = ""+CONFIG.htmlTilesetPaneH+"px"; document.getElementById("compositepane").style.maxWidth = ""+CONFIG.htmlCompositePaneW+"px"; document.getElementById("compositepane").style.maxHeight = ""+CONFIG.htmlCompositePaneH+"px"; // hide map tab let mappane = document.getElementById("map"); mappane.style.display = "none"; } // -- // Initialize handlers loading a PNG file into the composite window // -- export function initCompositePNGLoader() { const fileInput = document.getElementById('compositepng'); fileInput.onchange = (evt) => { if (!window.FileReader) return; // Browser is not compatible if (g_ctx.debug_flag) { console.log("compositepng ", fileInput.files[0].name); } let bgname = fileInput.files[0].name; const texture = PIXI.Texture.from("./"+bgname); const bg = new PIXI.Sprite(texture); bg.zIndex = 0; g_ctx.composite.container.addChild(bg); } } // -- // initailized handler to load a new tileset // -- export function initTilesetLoader(callme) { const fileInput = document.getElementById('tilesetfile'); fileInput.onchange = async (evt) => { if (!window.FileReader) return; // Browser is not compatible if (g_ctx.debug_flag) { console.log("spritesheet ", fileInput.files[0].name); } g_ctx.tilesetpath = "./spritesheets/"+fileInput.files[0].name; g_ctx.tiledimx = 16; g_ctx.tiledimy = 16; callme(); } } ================================================ FILE: src/editor/spritefile.js ================================================ import * as CONFIG from './seconfig.js' import * as UTIL from './eutils.js' import { g_ctx } from './secontext.js' // global context function generate_preamble() { const preamble = '' + '{"frames": {\n' + '\n'; return preamble; } // Function to download data to a file function download(data, filename, type) { var file = new Blob([data], {type: type}); if (window.navigator.msSaveOrOpenBlob) // IE10+ window.navigator.msSaveOrOpenBlob(file, filename); else { // Others var a = document.createElement("a"), url = URL.createObjectURL(file); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); setTimeout(function() { document.body.removeChild(a); window.URL.revokeObjectURL(url); }, 0); } } export function generate_sprite_file() { let layer0 = g_ctx.g_layers[0]; console.log("generate_sprite_file"); let text = generate_preamble(); let animations = Array.from(Array(CONFIG.leveltileheight), () => new Array().fill(null)); for (let row = 0; row < CONFIG.leveltileheight; row++) { if (!layer0.tilearray[row][0]) { // FIXME // Assume row is empty if first tile is. continue; } for (let x = 0; x < layer0.tilearray[row].length; x++) { //"pixels_large1.png": // { // "frame": {"x":0,"y":192,"w":32,"h":64}, // "rotated": false, // "trimmed": true, // "spriteSourceSize": {"x":0,"y":0,"w":32,"h":64}, // "sourceSize": {"w":32,"h":64} // }, let framename = '"tile' + row + "_" + x + '"'; animations[row].push(framename); let frame = layer0.tilearray[row][x]; text += framename + ": { \n"; text += '\t"frame": {'; text += '"x": '+ frame.tspx[0]+ ', "y": '+ frame.tspx[1]+ ', "w": '+ g_ctx.tiledimx+ ', "h": '+ g_ctx.tiledimy+ ' },\n'; text += '\t"rotated": false,\n'; text += '\t"trimmed": true,\n'; text += '\t"spriteSourceSize": {'; text += '"x":0, "y":0, "w": '+ g_ctx.tiledimx+ ', "h": '+ g_ctx.tiledimy+ ' },\n'; text += '\t"sourceSize": {'; text += '"w": '+ g_ctx.tiledimx+ ', "h": '+ g_ctx.tiledimy+ ' }\n'; text += '\t}'; text += (x === layer0.tilearray[row].length - 1)? '\n':',\n' } } text += '},\n'; text += '"animations": {\n'; for (let row = 0; row < CONFIG.leveltileheight; row++) { if(animations[row].length == 0) { continue; } text += '"row'+row+'" : ['; for (let x = 0; x < animations[row].length; x++){ text += ''+animations[row][x]; if (x < animations[row].length - 1){ text += ','; } } text += "],\n" } // remove the trailing comma text = text.slice(0,-2); text += '\n'; text += '},\n'; text += '"meta": {\n'; text += '\t"image": "'+ g_ctx.tilesetpath+'",\n' text += '\t"format": "RGBA8888",\n'; text += '\t"scale": "1"\n'; text += '}\n'; text += '}\n'; //console.log(text); let filename = g_ctx.tilesetpath.split('/').slice(-1)[0]; filename = filename.split('.')[0]; console.log("spritefile: saving to file ",filename); UTIL.download(text, filename+".json", "text/plain"); } ================================================ FILE: src/editor/undo.js ================================================ const UNDO_STAX_MAX_LEN = 16 let undo_stack = []; let undoqueu = []; export function undo_mark_task_start(layer) { undoqueu = []; undoqueu.push(layer); } export function undo_add_index_to_task(tileindex, oldValue) { undoqueu.push([tileindex, oldValue]); } export function undo_mark_task_end() { undo_stack.push(undoqueu); if (undo_stack.length > UNDO_STAX_MAX_LEN){ undo_stack.shift(); } } // utility function for adding a single tile as a task export function undo_add_single_index_as_task(layer, tileindex, oldValue) { undo_mark_task_start(layer); undo_add_index_to_task(tileindex, oldValue); undo_mark_task_end(); } export function undo_pop() { return undo_stack.pop(); } ================================================ FILE: src/editor/windmill.json ================================================ {"frames": { "pixels_large1.png": { "frame": {"x":0,"y":0,"w":208,"h":208}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":208,"h":208}, "sourceSize": {"w":208,"h":208} }, "pixels_large2.png": { "frame": {"x":208,"y":0,"w":208,"h":208}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":208,"h":208}, "sourceSize": {"w":208,"h":208} }, "pixels_large3.png": { "frame": {"x":416,"y":0,"w":208,"h":208}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":208,"h":208}, "sourceSize": {"w":208,"h":208} }, "pixels_large4.png": { "frame": {"x":0,"y":208,"w":208,"h":208}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":208,"h":208}, "sourceSize": {"w":208,"h":208} }, "pixels_large5.png": { "frame": {"x":208,"y":208,"w":208,"h":208}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":208,"h":208}, "sourceSize": {"w":208,"h":208} }, "pixels_large6.png": { "frame": {"x":416,"y":208,"w":208,"h":208}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":208,"h":208}, "sourceSize": {"w":208,"h":208} }, "pixels_large7.png": { "frame": {"x":0,"y":416,"w":208,"h":208}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":208,"h":208}, "sourceSize": {"w":208,"h":208} }, "pixels_large8.png": { "frame": {"x":208,"y":416,"w":208,"h":208}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":208,"h":208}, "sourceSize": {"w":208,"h":208} } }, "animations": { "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"] }, "meta": { "image": "./spritesheets/windmill.png", "format": "RGBA8888", "size": {"w":624,"h":624}, "scale": "1" } } ================================================ FILE: src/hooks/sendInput.ts ================================================ import { ConvexReactClient, useConvex } from 'convex/react'; import { InputArgs, InputReturnValue, Inputs } from '../../convex/aiTown/inputs'; import { api } from '../../convex/_generated/api'; import { Id } from '../../convex/_generated/dataModel'; export async function waitForInput(convex: ConvexReactClient, inputId: Id<'inputs'>) { const watch = convex.watchQuery(api.aiTown.main.inputStatus, { inputId }); let result = watch.localQueryResult(); // The result's undefined if the query's loading and null if the input hasn't // been processed yet. if (result === undefined || result === null) { let dispose: undefined | (() => void); try { await new Promise((resolve, reject) => { dispose = watch.onUpdate(() => { try { result = watch.localQueryResult(); } catch (e: any) { reject(e); return; } if (result !== undefined && result !== null) { resolve(); } }); }); } finally { if (dispose) { dispose(); } } } if (!result) { throw new Error(`Input ${inputId} was never processed.`); } if (result.kind === 'error') { throw new Error(result.message); } return result.value; } export function useSendInput( engineId: Id<'engines'>, name: Name, ): (args: InputArgs) => Promise> { const convex = useConvex(); return async (args) => { const inputId = await convex.mutation(api.world.sendWorldInput, { engineId, name, args }); return await waitForInput(convex, inputId); }; } ================================================ FILE: src/hooks/serverGame.ts ================================================ import { GameId } from '../../convex/aiTown/ids.ts'; import { AgentDescription } from '../../convex/aiTown/agentDescription.ts'; import { PlayerDescription } from '../../convex/aiTown/playerDescription.ts'; import { World } from '../../convex/aiTown/world.ts'; import { WorldMap } from '../../convex/aiTown/worldMap.ts'; import { Id } from '../../convex/_generated/dataModel'; import { useMemo } from 'react'; import { useQuery } from 'convex/react'; import { api } from '../../convex/_generated/api'; import { parseMap } from '../../convex/util/object.ts'; export type ServerGame = { world: World; playerDescriptions: Map, PlayerDescription>; agentDescriptions: Map, AgentDescription>; worldMap: WorldMap; }; // TODO: This hook reparses the game state (even if we're not rerunning the query) // when used in multiple components. Move this to a context to only parse it once. export function useServerGame(worldId: Id<'worlds'> | undefined): ServerGame | undefined { const worldState = useQuery(api.world.worldState, worldId ? { worldId } : 'skip'); const descriptions = useQuery(api.world.gameDescriptions, worldId ? { worldId } : 'skip'); const game = useMemo(() => { if (!worldState || !descriptions) { return undefined; } return { world: new World(worldState.world), agentDescriptions: parseMap( descriptions.agentDescriptions, AgentDescription, (p) => p.agentId, ), playerDescriptions: parseMap( descriptions.playerDescriptions, PlayerDescription, (p) => p.playerId, ), worldMap: new WorldMap(descriptions.worldMap), }; }, [worldState, descriptions]); return game; } ================================================ FILE: src/hooks/useHistoricalTime.ts ================================================ import { Doc } from '../../convex/_generated/dataModel'; import { useEffect, useRef, useState } from 'react'; export function useHistoricalTime(engineStatus?: Doc<'engines'>) { const timeManager = useRef(new HistoricalTimeManager()); const rafRef = useRef(); const [historicalTime, setHistoricalTime] = useState(undefined); if (engineStatus) { timeManager.current.receive(engineStatus); } const updateTime = (performanceNow: number) => { // We don't need sub-millisecond precision for interpolation, so just use `Date.now()`. const now = Date.now(); setHistoricalTime(timeManager.current.historicalServerTime(now)); rafRef.current = requestAnimationFrame(updateTime); }; useEffect(() => { rafRef.current = requestAnimationFrame(updateTime); return () => cancelAnimationFrame(rafRef.current!); }, []); return { historicalTime, timeManager: timeManager.current }; } type ServerTimeInterval = { startTs: number; endTs: number; }; export class HistoricalTimeManager { intervals: Array = []; prevClientTs?: number; prevServerTs?: number; totalDuration: number = 0; latestEngineStatus?: Doc<'engines'>; receive(engineStatus: Doc<'engines'>) { this.latestEngineStatus = engineStatus; if (!engineStatus.currentTime || !engineStatus.lastStepTs) { return; } const latest = this.intervals[this.intervals.length - 1]; if (latest) { if (latest.endTs === engineStatus.currentTime) { return; } if (latest.endTs > engineStatus.currentTime) { throw new Error(`Received out-of-order engine status`); } } const newInterval = { startTs: engineStatus.lastStepTs, endTs: engineStatus.currentTime, }; this.intervals.push(newInterval); this.totalDuration += newInterval.endTs - newInterval.startTs; } historicalServerTime(clientNow: number): number | undefined { if (this.intervals.length == 0) { return undefined; } if (clientNow === this.prevClientTs) { return this.prevServerTs; } // If this is our first time simulating, start at the beginning of the buffer. const prevClientTs = this.prevClientTs ?? clientNow; const prevServerTs = this.prevServerTs ?? this.intervals[0].startTs; const lastServerTs = this.intervals[this.intervals.length - 1].endTs; // Simple rate adjustment: run time at 1.2 speed if we're more than 1s behind and // 0.8 speed if we only have 100ms of buffer left. A more sophisticated approach // would be to continuously adjust the rate based on the size of the buffer. const bufferDuration = lastServerTs - prevServerTs; let rate = 1; if (bufferDuration < SOFT_MIN_SERVER_BUFFER_AGE) { rate = 0.8; } else if (bufferDuration > SOFT_MAX_SERVER_BUFFER_AGE) { rate = 1.2; } let serverTs = Math.max( prevServerTs + (clientNow - prevClientTs) * rate, // Jump forward if we're too far behind. lastServerTs - MAX_SERVER_BUFFER_AGE, ); let chosen = null; for (let i = 0; i < this.intervals.length; i++) { const snapshot = this.intervals[i]; // We're past this snapshot, continue to the next one. if (snapshot.endTs < serverTs) { continue; } // We're cleanly within this snapshot. if (serverTs >= snapshot.startTs) { chosen = i; break; } // We've gone past the desired timestamp, which implies a gap in our server state. // Jump time forward to the beginning of this snapshot. if (serverTs < snapshot.startTs) { serverTs = snapshot.startTs; chosen = i; } } if (chosen === null) { serverTs = this.intervals.at(-1)!.endTs; chosen = this.intervals.length - 1; } // Time only moves forward, so we can trim all of the snapshots before our chosen one. const toTrim = Math.max(chosen - 1, 0); if (toTrim > 0) { for (const snapshot of this.intervals.slice(0, toTrim)) { this.totalDuration -= snapshot.endTs - snapshot.startTs; } this.intervals = this.intervals.slice(toTrim); } this.prevClientTs = clientNow; this.prevServerTs = serverTs; return serverTs; } bufferHealth(): number { if (!this.intervals.length) { return 0; } const lastServerTs = this.prevServerTs ?? this.intervals[0].startTs; return this.intervals[this.intervals.length - 1].endTs - lastServerTs; } clockSkew(): number { if (!this.prevClientTs || !this.prevServerTs) { return 0; } return this.prevClientTs - this.prevServerTs; } } const MAX_SERVER_BUFFER_AGE = 1500; const SOFT_MAX_SERVER_BUFFER_AGE = 1250; const SOFT_MIN_SERVER_BUFFER_AGE = 250; ================================================ FILE: src/hooks/useHistoricalValue.ts ================================================ import { FieldConfig, History, unpackSampleRecord } from '../../convex/engine/historicalObject'; import { useMemo, useRef } from 'react'; export function useHistoricalValue>( fields: FieldConfig, historicalTime: number | undefined, value: T | undefined, history: ArrayBuffer | undefined, ): T | undefined { const manager = useRef(new HistoryManager()); const sampleRecord: Record | undefined = useMemo(() => { if (!value || !history) { return undefined; } if (!(history instanceof ArrayBuffer)) { throw new Error(`Expected ArrayBuffer, found ${typeof history}`); } return unpackSampleRecord(fields, history); }, [value && history]); if (sampleRecord) { manager.current.receive(sampleRecord); } if (value === undefined) { return undefined; } if (!historicalTime) { return value; } const historicalFields = manager.current.query(historicalTime); return { ...value, ...historicalFields }; } class HistoryManager { histories: Record = {}; receive(sampleRecord: Record) { for (const [fieldName, history] of Object.entries(sampleRecord)) { let histories = this.histories[fieldName]; if (!histories) { histories = []; this.histories[fieldName] = histories; } if (histories[histories.length - 1] == history) { continue; } histories.push(history); } } query(historicalTime: number): Record { const result: Record = {}; for (const [fieldName, histories] of Object.entries(this.histories)) { if (histories.length == 0) { continue; } let foundIndex = null; let currentValue = histories[0].initialValue; for (let i = 0; i < histories.length; i++) { const history = histories[i]; for (const sample of history.samples) { if (sample.time > historicalTime) { foundIndex = i; break; } currentValue = sample.value; } if (foundIndex !== null) { break; } } if (foundIndex !== null) { this.histories[fieldName] = histories.slice(foundIndex); } result[fieldName] = currentValue; } return result; } } ================================================ FILE: src/hooks/useWorldHeartbeat.ts ================================================ import { useMutation, useQuery } from 'convex/react'; import { useEffect } from 'react'; import { api } from '../../convex/_generated/api'; import { WORLD_HEARTBEAT_INTERVAL } from '../../convex/constants'; export function useWorldHeartbeat() { const worldStatus = useQuery(api.world.defaultWorldStatus); const worldId = worldStatus?.worldId; // Send a periodic heartbeat to our world to keep it alive. const heartbeat = useMutation(api.world.heartbeatWorld); useEffect(() => { const sendHeartBeat = () => { if (!worldStatus) { return; } // Don't send a heartbeat if we've observed one sufficiently close // to the present. if (Date.now() - WORLD_HEARTBEAT_INTERVAL / 2 < worldStatus.lastViewed) { return; } void heartbeat({ worldId: worldStatus.worldId }); }; sendHeartBeat(); const id = setInterval(sendHeartBeat, WORLD_HEARTBEAT_INTERVAL); return () => clearInterval(id); // Rerun if the `worldId` changes but not `worldStatus`, since don't want to // resend the heartbeat whenever its last viewed timestamp changes. }, [worldId, heartbeat]); } ================================================ FILE: src/index.css ================================================ @tailwind base; @tailwind components; @tailwind utilities; @font-face { font-family: 'Upheaval Pro'; src: url(/assets/fonts/upheaval_pro.ttf); } @font-face { font-family: 'VCR OSD Mono'; src: url(/assets/fonts/vcr_osd_mono.ttf); } .font-display { font-family: 'Upheaval Pro', 'sans-serif'; } .font-body { font-family: 'VCR OSD Mono', 'monospace'; } .font-system { font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; } :root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255; } @media (prefers-color-scheme: dark) { :root { --foreground-rgb: 255, 255, 255; --background-start-rgb: 0, 0, 0; --background-end-rgb: 0, 0, 0; } } body { color: rgb(var(--foreground-rgb)); background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb)); } .game-background { background: linear-gradient(rgba(41, 41, 41, 0.8), rgba(41, 41, 41, 0.8)), url(../assets/background.webp); background-blend-mode: hard-light; background-position: center; background-repeat: no-repeat; background-size: cover; background-attachment: fixed; } .game-title { background: linear-gradient(to bottom, #fec742, #dd7c42); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; filter: drop-shadow(0px 0.08em 0px #6e2146); } .game-frame { border-width: 36px; border-image-source: url(../assets/ui/frame.svg); border-image-repeat: stretch; border-image-slice: 25%; } .game-progress-bar { border: 5px solid rgb(23, 20, 33); } @keyframes moveStripes { to { background-position: calc(100% + 28px) 0; } } .game-progress-bar-progress { background: repeating-linear-gradient(135deg, white, white 10px, #dfdfdf 10px, #dfdfdf 20px); background-size: 200% 100%; background-position: 100% 0; animation: moveStripes 0.5s linear infinite; } @media screen and (min-width: 640px) { .game-frame { border-width: 48px; } } .shadow-solid { text-shadow: 0 0.1em 0 rgba(0, 0, 0, 0.5); } .bubble { border-width: 30px; border-image-source: url(../assets/ui/bubble-left.svg); border-image-repeat: stretch; border-image-slice: 20%; } .bubble-mine { border-image-source: url(../assets/ui/bubble-right.svg); } .box { border-width: 12px; border-image-source: url(../assets/ui/box.svg); border-image-repeat: stretch; border-image-slice: 12.5%; } .desc { border-width: 56px; border-image-source: url(../assets/ui/desc.svg); border-image-repeat: stretch; border-image-slice: 28%; } .chats { border-width: 24px; border-image-source: url(../assets/ui/chats.svg); border-image-repeat: stretch; border-image-slice: 40%; } .login-prompt { border-width: 48px; border-image-source: url(../assets/ui/jewel_box.svg); border-image-repeat: stretch; border-image-slice: 40%; } .button { border-width: 1em; border-image-source: url(../assets/ui/button.svg); border-image-repeat: stretch; border-image-slice: 25%; cursor: pointer; } .button span { display: inline-block; transform: translateY(-15%); } @media (max-width: 640px) { .button { height: 40px; border-width: 0.75em; font-size: 16px; } .button > div, .button > span { vertical-align: top; line-height: 1; } } .button:hover { opacity: 0.8; } .button:active { /* Inlining this image to avoid flashing during loading */ 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"); } .button:active span { transform: none; } p[contenteditable='true']:empty::before { content: attr(placeholder); color: #aaa; } .shape-top-left-corner { clip-path: polygon(0 0, 100% 0, 0 100%); } ================================================ FILE: src/main.tsx ================================================ import React from 'react'; import ReactDOM from 'react-dom/client'; import Home from './App.tsx'; import './index.css'; import 'uplot/dist/uPlot.min.css'; import 'react-toastify/dist/ReactToastify.css'; import ConvexClientProvider from './components/ConvexClientProvider.tsx'; ReactDOM.createRoot(document.getElementById('root')!).render( , ); ================================================ FILE: src/toasts.ts ================================================ import { toast } from 'react-toastify'; export async function toastOnError(promise: Promise): Promise { try { return await promise; } catch (error: any) { toast.error(error.message); throw error; } } ================================================ FILE: src/vite-env.d.ts ================================================ /// ================================================ FILE: tailwind.config.js ================================================ /** @type {import('tailwindcss').Config} */ module.exports = { content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], theme: { fontFamily: { display: ['var(--font-display)', 'sans-serif'], body: ['var(--font-body)', 'monospace'], }, extend: { colors: { brown: { 100: '#FFFFFF', 200: '#EAD4AA', 300: '#E4A672', 500: '#B86F50', 700: '#743F39', 800: '#3F2832', 900: '#181425', }, clay: { 100: '#C0CBDC', 300: '#8B9BB4', 500: '#5A6988', 700: '#3A4466', 900: '#181425', }, }, }, }, plugins: [require('@tailwindcss/forms')], }; ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "target": "es2015", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "allowImportingTsExtensions": true, "plugins": [ { "name": "next" } ], "paths": { "@/*": ["./src/*"] } }, "include": ["**/*.ts", "**/*.tsx", "**/*.js", "postcss.config.js"], "exclude": ["node_modules"] } ================================================ FILE: vercel.json ================================================ { "framework": "vite", "rewrites": [ { "source": "/ai-town/:match*", "destination": "/:match*" } ] } ================================================ FILE: vite.config.ts ================================================ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ base: '/ai-town', plugins: [react()], server: { allowedHosts: ['ai-town-your-app-name.fly.dev', 'localhost', '127.0.0.1'], }, });