diff --git a/patches/convex/_generated/api.d.ts b/patches/convex/_generated/api.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..9f01d2429bb0a0e334396b5de5bee35a83272e4c --- /dev/null +++ b/patches/convex/_generated/api.d.ts @@ -0,0 +1,125 @@ +/* eslint-disable */ +/** + * Generated `api` utility. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * Generated by convex@1.11.2. + * 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_gameCycle from "../aiTown/gameCycle.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_voting from "../aiTown/voting.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/gameCycle": typeof aiTown_gameCycle; + "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/voting": typeof aiTown_voting; + "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 +>; diff --git a/patches/convex/_generated/api.js b/patches/convex/_generated/api.js new file mode 100644 index 0000000000000000000000000000000000000000..21a4bd2cd621bb9a4eab39f9ea6bd5d48c2a001d --- /dev/null +++ b/patches/convex/_generated/api.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +/** + * Generated `api` utility. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * Generated by convex@1.11.2. + * 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; diff --git a/patches/convex/_generated/dataModel.d.ts b/patches/convex/_generated/dataModel.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..80dc17d947e55abfa264141b8fd4b5fcbf046751 --- /dev/null +++ b/patches/convex/_generated/dataModel.d.ts @@ -0,0 +1,61 @@ +/* eslint-disable */ +/** + * Generated data model types. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * Generated by convex@1.11.2. + * 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; diff --git a/patches/convex/_generated/server.d.ts b/patches/convex/_generated/server.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..c512cc05fbedb9ea091961e0c9d3e3f46990d30d --- /dev/null +++ b/patches/convex/_generated/server.d.ts @@ -0,0 +1,143 @@ +/* eslint-disable */ +/** + * Generated utilities for implementing server-side Convex query and mutation functions. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * Generated by convex@1.11.2. + * 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; diff --git a/patches/convex/_generated/server.js b/patches/convex/_generated/server.js new file mode 100644 index 0000000000000000000000000000000000000000..5318a3038ec39853f002992127f9994a7ffd47fb --- /dev/null +++ b/patches/convex/_generated/server.js @@ -0,0 +1,90 @@ +/* eslint-disable */ +/** + * Generated utilities for implementing server-side Convex query and mutation functions. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * Generated by convex@1.11.2. + * 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; diff --git a/patches/convex/agent/conversation.ts b/patches/convex/agent/conversation.ts index f460d74fa32af21e08b1700d4e385ae1b2148a05..855a6ffeb4ed22018b4fe45a5db4c9aace39190d 100644 --- a/patches/convex/agent/conversation.ts +++ b/patches/convex/agent/conversation.ts @@ -1,346 +1,345 @@ -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'>, -) { - 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.`, - ); - } - prompt.push(`${player.name}:`); - - const { content } = await chatCompletion({ - messages: [ - { - role: 'user', - content: prompt.join('\n'), - }, - ], - max_tokens: 300, - stream: true, - stop: stopWords(otherPlayer.name, player.name), - }); - return content; -} - -export async function continueConversationMessage( - ctx: ActionCtx, - worldId: Id<'worlds'>, - conversationId: GameId<'conversations'>, - playerId: GameId<'players'>, - otherPlayerId: GameId<'players'>, -) { - 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: 'user', - content: prompt.join('\n'), - }, - ...(await previousMessages( - ctx, - worldId, - player, - otherPlayer, - conversation.id as GameId<'conversations'>, - )), - ]; - llmMessages.push({ role: 'user', content: `${player.name}:` }); - - const { content } = await chatCompletion({ - messages: llmMessages, - max_tokens: 300, - stream: true, - stop: stopWords(otherPlayer.name, player.name), - }); - return content; -} - -export async function leaveConversationMessage( - ctx: ActionCtx, - worldId: Id<'worlds'>, - conversationId: GameId<'conversations'>, - playerId: GameId<'players'>, - otherPlayerId: GameId<'players'>, -) { - 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: 'user', - content: prompt.join('\n'), - }, - ...(await previousMessages( - ctx, - worldId, - player, - otherPlayer, - conversation.id as GameId<'conversations'>, - )), - ]; - llmMessages.push({ role: 'user', content: `${player.name}:` }); - - const { content } = await chatCompletion({ - messages: llmMessages, - max_tokens: 300, - stream: true, - stop: stopWords(otherPlayer.name, player.name), - }); - return content; -} - -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() + ':']); -} +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'>, +) { + 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.`, + ); + } + prompt.push(`${player.name}:`); + + const { content } = await chatCompletion({ + messages: [ + { + role: 'user', + content: prompt.join('\n'), + }, + ], + max_tokens: 300, + stream: true, + stop: stopWords(otherPlayer.name, player.name), + }); + return content; +} + +export async function continueConversationMessage( + ctx: ActionCtx, + worldId: Id<'worlds'>, + conversationId: GameId<'conversations'>, + playerId: GameId<'players'>, + otherPlayerId: GameId<'players'>, +) { + 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: 'user', + content: prompt.join('\n'), + }, + ...(await previousMessages( + ctx, + worldId, + player, + otherPlayer, + conversation.id as GameId<'conversations'>, + )), + ]; + llmMessages.push({ role: 'user', content: `${player.name}:` }); + + const { content } = await chatCompletion({ + messages: llmMessages, + max_tokens: 300, + stream: true, + stop: stopWords(otherPlayer.name, player.name), + }); + return content; +} + +export async function leaveConversationMessage( + ctx: ActionCtx, + worldId: Id<'worlds'>, + conversationId: GameId<'conversations'>, + playerId: GameId<'players'>, + otherPlayerId: GameId<'players'>, +) { + 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: 'user', + content: prompt.join('\n'), + }, + ...(await previousMessages( + ctx, + worldId, + player, + otherPlayer, + conversation.id as GameId<'conversations'>, + )), + ]; + llmMessages.push({ role: 'user', content: `${player.name}:` }); + + const { content } = await chatCompletion({ + messages: llmMessages, + max_tokens: 300, + stream: true, + stop: stopWords(otherPlayer.name, player.name), + }); + return content; +} + +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() + ':']); +} diff --git a/patches/convex/agent/embeddingsCache.ts b/patches/convex/agent/embeddingsCache.ts index 1c4583cd0e053a3f4e427f9b8e5bbde886ab7832..9aa1c6b5d40927130674b83d9ee6bbe26c30dd8c 100644 --- a/patches/convex/agent/embeddingsCache.ts +++ b/patches/convex/agent/embeddingsCache.ts @@ -1,110 +1,110 @@ -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; - }, -}); +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; + }, +}); diff --git a/patches/convex/agent/memory.ts b/patches/convex/agent/memory.ts index b049d52119ab63a0a14c430525f59f3b4e5ede97..25994567b544c0a3e957f6ec6dd73979e7d38d69 100644 --- a/patches/convex/agent/memory.ts +++ b/patches/convex/agent/memory.ts @@ -1,450 +1,450 @@ -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; -} +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; +} diff --git a/patches/convex/agent/schema.ts b/patches/convex/agent/schema.ts index be822bcee1d5bfe78c37661d85ef37958617adcb..b36e18ee8aea5d8e5d50576914fac3ca57380a3b 100644 --- a/patches/convex/agent/schema.ts +++ b/patches/convex/agent/schema.ts @@ -1,53 +1,53 @@ -import { v } from 'convex/values'; -import { playerId, conversationId } from '../aiTown/ids'; -import { defineTable } from 'convex/server'; -import { LLM_CONFIG } 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: LLM_CONFIG.embeddingDimension, - }), -}; - -export const agentTables = { - ...memoryTables, - embeddingsCache: defineTable({ - textHash: v.bytes(), - embedding: v.array(v.float64()), - }).index('text', ['textHash']), -}; +import { v } from 'convex/values'; +import { playerId, conversationId } from '../aiTown/ids'; +import { defineTable } from 'convex/server'; +import { LLM_CONFIG } 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: LLM_CONFIG.embeddingDimension, + }), +}; + +export const agentTables = { + ...memoryTables, + embeddingsCache: defineTable({ + textHash: v.bytes(), + embedding: v.array(v.float64()), + }).index('text', ['textHash']), +}; diff --git a/patches/convex/aiTown/agent.ts b/patches/convex/aiTown/agent.ts index a32022ce78a5326759ce483332b14d828c49b5c6..4182bbfbb1444f4ce1c8582e3fe959432ca09a07 100644 --- a/patches/convex/aiTown/agent.ts +++ b/patches/convex/aiTown/agent.ts @@ -1,368 +1,384 @@ -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; - }, -}); +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, + }; + } + + kill(game: Game, now: number) { + console.log(`agent ${ this.id } is killed`) + + // Remove schedule operation if any. + const operationId = this.inProgressOperation?.operationId; + if (operationId !== undefined) { + const index = game.pendingOperations.findIndex(op => op.args[0] === operationId); + + if (index !== -1) { + game.pendingOperations.splice(index, 1); + } + } + + game.world.agents.delete(this.id); + } + + 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; + }, +}); diff --git a/patches/convex/aiTown/agentDescription.ts b/patches/convex/aiTown/agentDescription.ts index 7205d798412332dfc1c8a6ede071ad15ed86dcfd..e53ff9ac69aa818adfcd026fb04e1432e8ebe847 100644 --- a/patches/convex/aiTown/agentDescription.ts +++ b/patches/convex/aiTown/agentDescription.ts @@ -1,27 +1,27 @@ -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; +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; diff --git a/patches/convex/aiTown/agentInputs.ts b/patches/convex/aiTown/agentInputs.ts index e7e61462f380638dba784296dccc88d9cd047af4..a3cd44fbe3ab0b4d3f59cbb41f9f9b07fd9144f2 100644 --- a/patches/convex/aiTown/agentInputs.ts +++ b/patches/convex/aiTown/agentInputs.ts @@ -1,155 +1,158 @@ -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 }; - }, - }), -}; +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'; +import { CharacterTypeSchema } from './playerDescription'; + +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(), + type: CharacterTypeSchema + }, + handler: (game, now, args) => { + const description = Descriptions[args.descriptionIndex]; + const playerId = Player.join( + game, + now, + description.name, + description.character, + description.identity, + args.type, + ); + 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 }; + }, + }), +}; diff --git a/patches/convex/aiTown/agentOperations.ts b/patches/convex/aiTown/agentOperations.ts index 6c1396eb3494b21984b2a9ae0d94e78c3bd5238e..907c489bd9a05425088fb5b0c0526dd11e12ad6b 100644 --- a/patches/convex/aiTown/agentOperations.ts +++ b/patches/convex/aiTown/agentOperations.ts @@ -1,182 +1,180 @@ -'use node'; - -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 completion = await completionFn( - ctx, - args.worldId, - args.conversationId as GameId<'conversations'>, - args.playerId as GameId<'players'>, - args.otherPlayerId as GameId<'players'>, - ); - // TODO: stream in the text instead of reading it all at once. - const text = await completion.readAll(); - - 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)), - }; -} +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 completion = await completionFn( + ctx, + args.worldId, + args.conversationId as GameId<'conversations'>, + args.playerId as GameId<'players'>, + args.otherPlayerId as GameId<'players'>, + ); + // TODO: stream in the text instead of reading it all at once. + const text = await completion.readAll(); + + 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)), + }; +} diff --git a/patches/convex/aiTown/conversation.ts b/patches/convex/aiTown/conversation.ts index ccfa1d559e845a88a56c62881111c95f14084fd4..fc93d5643d0f8cf4c3576c8e2a0d4324fac8a24d 100644 --- a/patches/convex/aiTown/conversation.ts +++ b/patches/convex/aiTown/conversation.ts @@ -1,395 +1,395 @@ -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; - }, - }), -}; +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; + }, + }), +}; diff --git a/patches/convex/aiTown/conversationMembership.ts b/patches/convex/aiTown/conversationMembership.ts index 7dd5eaf56f41a2ed10d9607b6c6d183379330e96..75d1cdfea234dfdb2801a88bdf661cb8971c34a0 100644 --- a/patches/convex/aiTown/conversationMembership.ts +++ b/patches/convex/aiTown/conversationMembership.ts @@ -1,38 +1,38 @@ -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, - }; - } -} +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, + }; + } +} diff --git a/patches/convex/aiTown/dayNightCycle.ts b/patches/convex/aiTown/dayNightCycle.ts deleted file mode 100644 index 638a08ce5af054efc95f602f9203e48c0e8a7502..0000000000000000000000000000000000000000 --- a/patches/convex/aiTown/dayNightCycle.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { v, Infer, ObjectType } from 'convex/values'; -import { Game } from './game'; - -// Define the schema for DayNightCycle -export const dayNightCycleSchema = { - currentTime: v.number(), - isDay: v.boolean(), - dayDuration: v.number(), - nightDuration: v.number(), -}; -export type SerializedDayNightCycle = ObjectType; - -export class DayNightCycle { - currentTime: number; - isDay: boolean; - dayDuration: number; - nightDuration: number; - - constructor(serialized: SerializedDayNightCycle) { - const { currentTime, isDay, dayDuration, nightDuration } = serialized; - this.currentTime = currentTime; - this.isDay = isDay; - this.dayDuration = dayDuration; - this.nightDuration = nightDuration; - } - - // Tick method to increment the counter - tick(game: Game, tickDuration: number) { - this.currentTime += tickDuration; - - if (this.isDay && this.currentTime >= this.dayDuration) { - this.isDay = false; - this.currentTime = 0; - this.onNightStart(game); - } else if (!this.isDay && this.currentTime >= this.nightDuration) { - this.isDay = true; - this.currentTime = 0; - this.onDayStart(game); - } - } - - onDayStart(game: Game) { - console.log("Day has started!"); - for (const player of game.world.players.values()) { - // player.onDayStart(game); - } - for (const agent of game.world.agents.values()) { - // agent.onDayStart(game); - } - } - - onNightStart(game: Game) { - console.log("Night has started!"); - for (const player of game.world.players.values()) { - // player.onNightStart(game); - } - for (const agent of game.world.agents.values()) { - // agent.onNightStart(game); - } - } - - serialize(): SerializedDayNightCycle { - const { currentTime, isDay, dayDuration, nightDuration } = this; - return { - currentTime, - isDay, - dayDuration, - nightDuration, - }; - } -} diff --git a/patches/convex/aiTown/game.ts b/patches/convex/aiTown/game.ts index 080016136371ee470f573e290714a6133d43e5c7..ab1b22f9410daaeefb10ded2ed967f661c25952a 100644 --- a/patches/convex/aiTown/game.ts +++ b/patches/convex/aiTown/game.ts @@ -1,374 +1,374 @@ -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) { - // update day&nigth cycle counter - this.world.dayNightCycle.tick(this, this.tickDuration); - - 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); - }, -}); +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) { + // update game cycle counter + this.world.gameCycle.tick(this, this.tickDuration); + + 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); + }, +}); diff --git a/patches/convex/aiTown/gameCycle.ts b/patches/convex/aiTown/gameCycle.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b94fdc19ccb2b254ef15723ce0b3cc33c0358dc --- /dev/null +++ b/patches/convex/aiTown/gameCycle.ts @@ -0,0 +1,97 @@ +import { v, Infer, ObjectType } from 'convex/values'; +import { Game } from './game'; +import { + DAY_DURATION, + NIGHT_DURATION, + WWOLF_VOTE_DURATION, + PLAYER_KILL_VOTE_DURATION, + LLM_VOTE_DURATION, +} from '../constants'; +import { processVotes } from './voting'; + +type CycleState = 'Day' | 'Night' | 'WerewolfVoting' | 'PlayerKillVoting' | 'LLMsVoting' | 'LobbyState' + +const stateDurations: { [key in CycleState]: number } = { + Day: DAY_DURATION, + Night: NIGHT_DURATION, + WerewolfVoting: WWOLF_VOTE_DURATION, + PlayerKillVoting: PLAYER_KILL_VOTE_DURATION, + LLMsVoting: LLM_VOTE_DURATION, + LobbyState: Infinity +}; + +const normalCycle: CycleState[] = [ + 'Day', + 'Night', + 'WerewolfVoting', + 'PlayerKillVoting', +]; + + +export const gameCycleSchema = { + currentTime: v.number(), + cycleState: v.union( + v.literal('Day'), + v.literal('Night'), + v.literal('WerewolfVoting'), + v.literal('PlayerKillVoting'), + v.literal('LLMsVoting'), + v.literal('LobbyState'), + ), + cycleIndex: v.number(), +}; + +export type SerializedGameCycle = ObjectType; + +const onStateChange = (prevState: CycleState, newState: CycleState, game: Game, now: number) => { + console.log(`state changed: ${ prevState } -> ${ newState }`); + if (prevState === 'PlayerKillVoting') { + const mostVotedPlayer = processVotes(game.world.votes, [...game.world.players.values()])[0]; + // TODO: Kill the player + const playerToKill = game.world.players.get(mostVotedPlayer) + if (playerToKill != undefined) { + playerToKill.kill(game, now) + } + } + if (prevState === 'WerewolfVoting') { + const mostVotedPlayer = processVotes(game.world.votes, [...game.world.players.values()])[0]; + // TODO: Check if most voted player is werewolf + } + // TODO: Implement LLM voting +}; + +export class GameCycle { + currentTime: number; + cycleState: CycleState; + cycleIndex: number; + + constructor(serialized: SerializedGameCycle) { + const { currentTime, cycleState, cycleIndex } = serialized; + this.currentTime = currentTime; + this.cycleState = cycleState; + this.cycleIndex = cycleIndex; + } + + // Tick method to increment the counter + tick(game: Game, tickDuration: number) { + this.currentTime += tickDuration; + + if (this.currentTime >= stateDurations[this.cycleState]) { + const prevState = this.cycleState; + this.currentTime = 0; + this.cycleIndex = (this.cycleIndex + 1) % normalCycle.length; + this.cycleState = normalCycle[this.cycleIndex]; + onStateChange(prevState, this.cycleState, game, tickDuration); + } +} + + + serialize(): SerializedGameCycle { + const { currentTime, cycleState, cycleIndex } = this; + return { + currentTime, + cycleState, + cycleIndex, + }; + } +} diff --git a/patches/convex/aiTown/ids.ts b/patches/convex/aiTown/ids.ts index 0592e7c1141a264a3701e6ca1281f15004dd5822..2f20d0758cee8e58d9e673c2cf0081cf8160d196 100644 --- a/patches/convex/aiTown/ids.ts +++ b/patches/convex/aiTown/ids.ts @@ -1,32 +1,32 @@ -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(); +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(); diff --git a/patches/convex/aiTown/inputHandler.ts b/patches/convex/aiTown/inputHandler.ts index 940bf59d3220d29d6bd77d66646a18fe007fe0b0..f1a26d1dc9191ecb4aba25c025905773b7836413 100644 --- a/patches/convex/aiTown/inputHandler.ts +++ b/patches/convex/aiTown/inputHandler.ts @@ -1,9 +1,9 @@ -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; -} +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; +} diff --git a/patches/convex/aiTown/inputs.ts b/patches/convex/aiTown/inputs.ts index 144009d74fb38b31bc0e0c0407662926675eb365..2e4bf00ed30e80ab024f280fa53f9d0e3aac4741 100644 --- a/patches/convex/aiTown/inputs.ts +++ b/patches/convex/aiTown/inputs.ts @@ -1,25 +1,25 @@ -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; +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; diff --git a/patches/convex/aiTown/insertInput.ts b/patches/convex/aiTown/insertInput.ts index 26d8d297ad14972e6dd012aef6d47e7983206b38..3b4fa76f63120d60c3b29b0413dc87a8ecad8aec 100644 --- a/patches/convex/aiTown/insertInput.ts +++ b/patches/convex/aiTown/insertInput.ts @@ -1,20 +1,20 @@ -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); -} +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); +} diff --git a/patches/convex/aiTown/location.ts b/patches/convex/aiTown/location.ts index c66c6a76781f874d78f3ed53d27274056b1195f6..9ec86f6f709b48e980481c8671acf3b1f6ce6366 100644 --- a/patches/convex/aiTown/location.ts +++ b/patches/convex/aiTown/location.ts @@ -1,32 +1,32 @@ -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, - }; -} +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, + }; +} diff --git a/patches/convex/aiTown/main.ts b/patches/convex/aiTown/main.ts index f0d7617beb90d4a835e22525b2d52711fe854719..5cc477f338e25533a6b4b32decb7e34e39f59c95 100644 --- a/patches/convex/aiTown/main.ts +++ b/patches/convex/aiTown/main.ts @@ -1,154 +1,154 @@ -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; - }, -}); +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; + }, +}); diff --git a/patches/convex/aiTown/movement.ts b/patches/convex/aiTown/movement.ts index 1a552ae17620bd206e8e9260fa34eeae4a68bd0c..f33804ac06b539e205c7b9759410270ca8cd2cba 100644 --- a/patches/convex/aiTown/movement.ts +++ b/patches/convex/aiTown/movement.ts @@ -1,189 +1,189 @@ -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; -} +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; +} diff --git a/patches/convex/aiTown/player.ts b/patches/convex/aiTown/player.ts index 4ed33e2c93ff854111f74092aa32ca7f65cb4610..fe0dae06c6f46d820d07d5d4ab9f627e552ea9e7 100644 --- a/patches/convex/aiTown/player.ts +++ b/patches/convex/aiTown/player.ts @@ -1,314 +1,342 @@ -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, - role?: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, - }), - ); - console.log("role",role) - game.playerDescriptions.set( - playerId, - new PlayerDescription({ - playerId, - character, - description, - name, - role, - }), - ); - 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()), - role:v.string(), - }, - handler: (game, now, args) => { - Player.join(game, now, args.name, args.character, args.description,args.role, 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; - }, - }), -}; +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 { CharacterType, CharacterTypeSchema, 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, + type: CharacterType, + 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, + type, + }), + ); + 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); + } + + kill(game: Game, now: number) { + const playerId = this.id + console.log(`player ${ playerId } is killed`) + // first leave: + this.leave(game, now) + + // if the player is npc, kill agent as well + const agent = [...game.world.agents.values()].find( + agent => agent.playerId === playerId + ) + + if (agent) { + agent.kill(game, now) + } + } + + 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()), + type: CharacterTypeSchema + }, + handler: (game, now, args) => { + Player.join(game, now, args.name, args.character, args.description, args.type ,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; + }, + }), + + vote: inputHandler({ + args: { + votedPlayerId: v.string(), + voteType: v.string(), + }, + handler: (game, now, args) => { + const votedPlayerId = parseGameId('players', args.votedPlayerId); + // TODO: Implement the fucntion + // game.vote(votedPlayerId); + return null; + }, + }), +}; diff --git a/patches/convex/aiTown/playerDescription.ts b/patches/convex/aiTown/playerDescription.ts index 6548c8e98684c424893ae961b856bb9cc9d1abb3..534cfa44558e0811bb4249cea5e6d280ae69ce21 100644 --- a/patches/convex/aiTown/playerDescription.ts +++ b/patches/convex/aiTown/playerDescription.ts @@ -1,39 +1,42 @@ -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(), - role:v.string() -}; -export type SerializedPlayerDescription = ObjectType; - -export class PlayerDescription { - playerId: GameId<'players'>; - name: string; - description: string; - character: string; - role:string; - - constructor(serialized: SerializedPlayerDescription) { - const { playerId, name, description, character,role } = serialized; - this.playerId = parseGameId('players', playerId); - this.name = name; - this.description = description; - this.character = character; - this.character=role; - } - - serialize(): SerializedPlayerDescription { - const { playerId, name, description, character, role} = this; - return { - playerId, - name, - description, - character, - role - }; - } -} +import { ObjectType, v } from 'convex/values'; +import { GameId, parseGameId, playerId } from './ids'; + +export type CharacterType = 'villager' | 'werewolf'; +export const CharacterTypeSchema = v.union(v.literal('villager'), v.literal('werewolf')); + +export const serializedPlayerDescription = { + playerId, + name: v.string(), + description: v.string(), + character: v.string(), + type: CharacterTypeSchema, +}; +export type SerializedPlayerDescription = ObjectType; + +export class PlayerDescription { + playerId: GameId<'players'>; + name: string; + description: string; + character: string; + type: CharacterType; + + constructor(serialized: SerializedPlayerDescription) { + const { playerId, name, description, character, type } = serialized; + this.playerId = parseGameId('players', playerId); + this.name = name; + this.description = description; + this.character = character; + this.type = type; + } + + serialize(): SerializedPlayerDescription { + const { playerId, name, description, character, type } = this; + return { + playerId, + name, + description, + type: type, + character, + }; + } +} diff --git a/patches/convex/aiTown/schema.ts b/patches/convex/aiTown/schema.ts index 20cdc40986437325fd92dc150f9d270ab9c90b9f..55f2b6189fb6c7a86902650565811d9e13dafb24 100644 --- a/patches/convex/aiTown/schema.ts +++ b/patches/convex/aiTown/schema.ts @@ -1,79 +1,79 @@ -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']), -}; +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']), +}; diff --git a/patches/convex/aiTown/voting.ts b/patches/convex/aiTown/voting.ts new file mode 100644 index 0000000000000000000000000000000000000000..7165a9ce191fe98d5755b38b3949bbcdd6ed8399 --- /dev/null +++ b/patches/convex/aiTown/voting.ts @@ -0,0 +1,58 @@ +import { ObjectType, v } from "convex/values"; +import { GameId, parseGameId, playerId } from "./ids"; +import { Player } from "./player"; + +export type VoteType = 'WarewolfVote' | 'PlayerKill' | 'LLMVote' + +export const VotesSchema = { + votesType: v.string(), + votes: v.array(v.object({ + playerId: playerId, + voter: playerId, + })) +} + +export type SerializedVotes = ObjectType; +export class Votes { + votesType: string; + votes: { + playerId: GameId<'players'>; + voter: GameId<'players'>; + }[]; + + constructor(serialized: SerializedVotes) { + const { votesType, votes } = serialized; + + this.votesType = votesType; + this.votes = votes.map((vote) => ({ + playerId: parseGameId('players', vote.playerId), + voter: parseGameId('players', vote.voter), + })); + } + + serialize(): SerializedVotes { + const { votesType, votes } = this; + return { + votesType, + votes, + }; + } +} + +export const processVotes = (votes: Votes, players: Player[], k: number = 1) => { + // Select the players with the most votes + const voteCounts: Record, number> = {}; + players.forEach(player => { + voteCounts[player.id] = 0; + }); + + // Tally the votes + votes.votes.forEach(vote => { + voteCounts[vote.playerId] = (voteCounts[vote.playerId] || 0) + 1; + }); + + const sortedVoteCounts = Object.entries(voteCounts).sort((a, b) => b[1] - a[1]); + const topKPlayers = sortedVoteCounts.slice(0, k).map(entry => entry[0]); + return topKPlayers as GameId<'players'>[]; + } + diff --git a/patches/convex/aiTown/world.ts b/patches/convex/aiTown/world.ts index c107fe9a980677a4ec19badd65c84905fad9c1be..6b18613e8df0e7fc4050afd15a854d2a148d83dc 100644 --- a/patches/convex/aiTown/world.ts +++ b/patches/convex/aiTown/world.ts @@ -1,70 +1,75 @@ -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'; -import { DayNightCycle, SerializedDayNightCycle, dayNightCycleSchema } from './dayNightCycle'; - -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), - dayNightCycle: v.object(dayNightCycleSchema), -}; -export type SerializedWorld = ObjectType; - -export class World { - nextId: number; - conversations: Map, Conversation>; - players: Map, Player>; - agents: Map, Agent>; - historicalLocations?: Map, ArrayBuffer>; - dayNightCycle: DayNightCycle; - - 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); - this.dayNightCycle = new DayNightCycle(serialized.dayNightCycle); - - 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, - })), - dayNightCycle: this.dayNightCycle.serialize(), - }; - } -} +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'; +import { GameCycle, gameCycleSchema } from './gameCycle'; +import { Votes, VotesSchema } from './voting'; + +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), + gameCycle: v.object(gameCycleSchema), + votes: v.object(VotesSchema) +}; +export type SerializedWorld = ObjectType; + +export class World { + nextId: number; + conversations: Map, Conversation>; + players: Map, Player>; + agents: Map, Agent>; + historicalLocations?: Map, ArrayBuffer>; + gameCycle: GameCycle; + votes: Votes; + + 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); + this.gameCycle = new GameCycle(serialized.gameCycle); + this.votes = new Votes(serialized.votes); + + 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, + })), + gameCycle: this.gameCycle.serialize(), + votes: this.votes.serialize(), + }; + } +} diff --git a/patches/convex/aiTown/worldMap.ts b/patches/convex/aiTown/worldMap.ts index 7d16bcdc9eca5345466eafc6ebf6d308c4b8400c..4406689de88896143be988492613301298551a75 100644 --- a/patches/convex/aiTown/worldMap.ts +++ b/patches/convex/aiTown/worldMap.ts @@ -1,94 +1,91 @@ -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(), - tileSetAlternateUrl: 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()))), - decorTiles: v.array(v.array(v.array(v.number()))), - objectTiles: v.array(tileLayer), - bgTilesN: v.array(v.array(v.array(v.number()))), - decorTilesN: v.array(v.array(v.array(v.number()))), - objectTilesN: v.array(tileLayer), - animatedSprites: v.array(v.object(animatedSprite)), -}; -export type SerializedWorldMap = ObjectType; - -export class WorldMap { - width: number; - height: number; - - tileSetUrl: string; - tileSetAlternateUrl: string; - tileSetDimX: number; - tileSetDimY: number; - - tileDim: number; - - bgTiles: TileLayer[]; - decorTiles: TileLayer[]; - objectTiles: TileLayer[]; - bgTilesN: TileLayer[]; - decorTilesN: TileLayer[]; - objectTilesN: TileLayer[]; - animatedSprites: AnimatedSprite[]; - - constructor(serialized: SerializedWorldMap) { - this.width = serialized.width; - this.height = serialized.height; - this.tileSetUrl = serialized.tileSetUrl; - this.tileSetAlternateUrl = serialized.tileSetAlternateUrl; - this.tileSetDimX = serialized.tileSetDimX; - this.tileSetDimY = serialized.tileSetDimY; - this.tileDim = serialized.tileDim; - this.bgTiles = serialized.bgTiles; - this.decorTiles = serialized.decorTiles; - this.objectTiles = serialized.objectTiles; - this.bgTilesN = serialized.bgTilesN; - this.decorTilesN = serialized.decorTilesN; - this.objectTilesN = serialized.objectTilesN; - this.animatedSprites = serialized.animatedSprites; - } - - serialize(): SerializedWorldMap { - return { - width: this.width, - height: this.height, - tileSetUrl: this.tileSetUrl, - tileSetAlternateUrl: this.tileSetAlternateUrl, - tileSetDimX: this.tileSetDimX, - tileSetDimY: this.tileSetDimY, - tileDim: this.tileDim, - bgTiles: this.bgTiles, - objectTiles: this.objectTiles, - decorTiles:this.decorTiles, - bgTilesN: this.bgTilesN, - objectTilesN: this.objectTilesN, - decorTilesN:this.decorTilesN, - animatedSprites: this.animatedSprites, - }; - } -} +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()))), + decorTiles: v.array(v.array(v.array(v.number()))), + objectTiles: v.array(tileLayer), + bgTilesN: v.array(v.array(v.array(v.number()))), + decorTilesN: v.array(v.array(v.array(v.number()))), + objectTilesN: 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[]; + decorTiles: TileLayer[]; + objectTiles: TileLayer[]; + bgTilesN: TileLayer[]; + decorTilesN: TileLayer[]; + objectTilesN: 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.decorTiles = serialized.decorTiles; + this.objectTiles = serialized.objectTiles; + this.bgTilesN = serialized.bgTilesN; + this.decorTilesN = serialized.decorTilesN; + this.objectTilesN = serialized.objectTilesN; + 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, + decorTiles:this.decorTiles, + bgTilesN: this.bgTilesN, + objectTilesN: this.objectTilesN, + decorTilesN:this.decorTilesN, + animatedSprites: this.animatedSprites, + }; + } +} diff --git a/patches/convex/constants.ts b/patches/convex/constants.ts index 3e747fe6ce09aa3b92bc9b4a488a18d229209c82..21e2c837ab8e6de989cd8ba9fdf15fd95fb5f0d9 100644 --- a/patches/convex/constants.ts +++ b/patches/convex/constants.ts @@ -1,81 +1,90 @@ -// 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 = 1; - -// 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; - -export const DAY_DURATION = 120000; -export const NIGHT_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'; +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; +export const DAY_DURATION = 60000; +export const NIGHT_DURATION = 60000; +export const WWOLF_VOTE_DURATION = 30000; +export const PLAYER_KILL_VOTE_DURATION = 30000; +export const LLM_VOTE_DURATION = 60000; + +// Debugging +// export const DAY_DURATION = 100; +// export const NIGHT_DURATION = 100; +// export const WWOLF_VOTE_DURATION = 100; +// export const PLAYER_KILL_VOTE_DURATION = 100; +// export const LLM_VOTE_DURATION = 100; + +// Bound the number of pathfinding searches we do per game step. +export const MAX_PATHFINDS_PER_STEP = 16; + +export const DEFAULT_NAME = 'Me'; diff --git a/patches/convex/crons.ts b/patches/convex/crons.ts index 9e6c1977a1f82ef660eee97dc9d0c9d2d06ecdfa..155238cc5f345b5cdd8db03e8ba292908a6f9452 100644 --- a/patches/convex/crons.ts +++ b/patches/convex/crons.ts @@ -1,89 +1,89 @@ -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}`); - } - }, -}); +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}`); + } + }, +}); diff --git a/patches/convex/engine/abstractGame.ts b/patches/convex/engine/abstractGame.ts index e2c6fb04d529d2f81a5471e9a33621c0554a6916..0ff3662d30bb0603e602f2d69c58d275f63881db 100644 --- a/patches/convex/engine/abstractGame.ts +++ b/patches/convex/engine/abstractGame.ts @@ -1,200 +1,199 @@ -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; - console.log("args",args) - 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); - } -} +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); + } +} diff --git a/patches/convex/engine/historicalObject.test.ts b/patches/convex/engine/historicalObject.test.ts index 312651bd6538d960233d13b1204dd40ee402dc78..2384c5d5e14f9c72b070520116ca9e9f50c1f263 100644 --- a/patches/convex/engine/historicalObject.test.ts +++ b/patches/convex/engine/historicalObject.test.ts @@ -1,47 +1,47 @@ -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); - } - } - }); -}); +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); + } + } + }); +}); diff --git a/patches/convex/engine/historicalObject.ts b/patches/convex/engine/historicalObject.ts index d9f9f6130df39e5e90dd5de16dbc86371ebc86ca..33858c69f0cffd51453d1ee48fc7a82a9c0b093f 100644 --- a/patches/convex/engine/historicalObject.ts +++ b/patches/convex/engine/historicalObject.ts @@ -1,355 +1,355 @@ -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)); -} +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)); +} diff --git a/patches/convex/engine/schema.ts b/patches/convex/engine/schema.ts index 79f8dff6feb28f7fd479c4bcf43eebaebd06c0d5..87c4083bb7f23317602bbb712abe41aaee27bdbd 100644 --- a/patches/convex/engine/schema.ts +++ b/patches/convex/engine/schema.ts @@ -1,56 +1,56 @@ -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), -}; +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), +}; diff --git a/patches/convex/http.ts b/patches/convex/http.ts index 9012b5887deaec70e1ff6be97a912b9bedca4628..61b3bdee6dcbe1e6606adfbc0515cc80dc9e637f 100644 --- a/patches/convex/http.ts +++ b/patches/convex/http.ts @@ -1,10 +1,10 @@ -import { httpRouter } from 'convex/server'; -import { handleReplicateWebhook } from './music'; - -const http = httpRouter(); -http.route({ - path: '/replicate_webhook', - method: 'POST', - handler: handleReplicateWebhook, -}); -export default http; +import { httpRouter } from 'convex/server'; +import { handleReplicateWebhook } from './music'; + +const http = httpRouter(); +http.route({ + path: '/replicate_webhook', + method: 'POST', + handler: handleReplicateWebhook, +}); +export default http; diff --git a/patches/convex/init.ts b/patches/convex/init.ts index 7bbe0fa8ea782443e748b3674dacb74a0a1dd041..10c819df46dcf4f75db2a6e3af9781e98a18bc49 100644 --- a/patches/convex/init.ts +++ b/patches/convex/init.ts @@ -1,125 +1,128 @@ -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, DAY_DURATION, NIGHT_DURATION } from './constants'; -import { assertApiKey } from './util/llm'; - -const init = mutation({ - args: { - numAgents: v.optional(v.number()), - }, - handler: async (ctx, args) => { - assertApiKey(); - 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: [], - // initialize day & night cycle counter - dayNightCycle: { - currentTime: 0, - isDay: true, - dayDuration: DAY_DURATION, - nightDuration: NIGHT_DURATION, - }, - }); - 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, - tileSetAlternateUrl: map.tilesetalternatepath, - tileSetDimX: map.tilesetpxw, - tileSetDimY: map.tilesetpxh, - tileDim: map.tiledim, - bgTiles: map.bgtiles, - objectTiles: map.objmap, - decorTiles: map.decors, - bgTilesN: map.bgtilesN, - objectTilesN: map.objmapN, - decorTilesN: map.decorsN, - 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)) - .collect(); - if (unactionedJoinInputs.length > 0) { - return false; - } - return true; -} +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 { assertApiKey } from './util/llm'; + +const init = mutation({ + args: { + numAgents: v.optional(v.number()), + }, + handler: async (ctx, args) => { + assertApiKey(); + 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, + type: 'villager', + }); + } + } + }, +}); +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: [], + // initialize game cycle counter + gameCycle: { + currentTime: 0, + cycleState: 'Day', + cycleIndex: 0, + }, + votes: { + votesType: 'KillVotes', + votes: [], + }, + }); + 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, + decorTiles: map.decors, + bgTilesN: map.bgtilesN, + objectTilesN: map.objmapN, + decorTilesN: map.decorsN, + 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)) + .collect(); + if (unactionedJoinInputs.length > 0) { + return false; + } + return true; +} diff --git a/patches/convex/messages.ts b/patches/convex/messages.ts index 67bb42ae61f65546d8ebacb22bb16289db2999d0..2a6e9c9fa954c323bdcc84c71c197f27b5b866c7 100644 --- a/patches/convex/messages.ts +++ b/patches/convex/messages.ts @@ -1,53 +1,53 @@ -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(), - }); - }, -}); +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(), + }); + }, +}); diff --git a/patches/convex/music.ts b/patches/convex/music.ts index 3c42607e21be706f657859996c730e1e35718e2d..01220ecb62ad18c2afa1775951a247a421d4e923 100644 --- a/patches/convex/music.ts +++ b/patches/convex/music.ts @@ -1,135 +1,135 @@ -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 '/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, - }); -} +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, + }); +} diff --git a/patches/convex/schema.ts b/patches/convex/schema.ts index b5dd400020c29d6bd93ac2014ee0b887503ad79f..a573b81dc76179e644ed3240957e13c16563e1fb 100644 --- a/patches/convex/schema.ts +++ b/patches/convex/schema.ts @@ -1,27 +1,27 @@ -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, -}); +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, +}); diff --git a/patches/convex/testing.ts b/patches/convex/testing.ts index 8ac0a72c02ef52a453680d4864510da0f73e4cca..c0ff8bc49cec02f4861d6ae13f52a98468c4aead 100644 --- a/patches/convex/testing.ts +++ b/patches/convex/testing.ts @@ -1,202 +1,203 @@ -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, LLM_CONFIG } 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(); - }, -}); +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, LLM_CONFIG } 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)}`, + type: 'villager', + }); + } + }, +}); + +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(); + }, +}); diff --git a/patches/convex/util/FastIntegerCompression.ts b/patches/convex/util/FastIntegerCompression.ts index e240fd6e305b159c09a3968d6ec20a43b0472654..d489a7797bf0cbb9910e66548e60ca9a5e5f30f7 100644 --- a/patches/convex/util/FastIntegerCompression.ts +++ b/patches/convex/util/FastIntegerCompression.ts @@ -1,221 +1,221 @@ -/** - * 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; -} +/** + * 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; +} diff --git a/patches/convex/util/assertNever.ts b/patches/convex/util/assertNever.ts index 110924d4b402f1cee53bb4bb29ee5c09460c089f..090005d08a409908ef671a9dacc3200e8b60f0fc 100644 --- a/patches/convex/util/assertNever.ts +++ b/patches/convex/util/assertNever.ts @@ -1,4 +1,4 @@ -// 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)}`); -} +// 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)}`); +} diff --git a/patches/convex/util/asyncMap.test.ts b/patches/convex/util/asyncMap.test.ts index e87d1975dbfbcad0ad6bb57b827e5d2ebbcb8866..f6ced49fd4650155e13f3ae51172d20ff7ea4599 100644 --- a/patches/convex/util/asyncMap.test.ts +++ b/patches/convex/util/asyncMap.test.ts @@ -1,15 +1,15 @@ -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([]); - }); +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([]); + }); }); \ No newline at end of file diff --git a/patches/convex/util/asyncMap.ts b/patches/convex/util/asyncMap.ts index ca28fb44a84e2e270eed5ceb2f6723ae1674f7da..6970ff71bd9169bb185530a2a4f0198e4dd65a47 100644 --- a/patches/convex/util/asyncMap.ts +++ b/patches/convex/util/asyncMap.ts @@ -1,20 +1,20 @@ -/** - * 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); -} +/** + * 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); +} diff --git a/patches/convex/util/compression.test.ts b/patches/convex/util/compression.test.ts index 4ed56693faa34ae0564461884a12117d9776f9b3..722c83b6d00fa96cd5efe7dec57a07b79b6605a3 100644 --- a/patches/convex/util/compression.test.ts +++ b/patches/convex/util/compression.test.ts @@ -1,90 +1,90 @@ -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); - } - }); -}); +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); + } + }); +}); diff --git a/patches/convex/util/compression.ts b/patches/convex/util/compression.ts index a9e639e1a4ab9801bd22fbeafa45478b8accd470..04278e1125228a601e94a4a79afb5f4bc08b4b1e 100644 --- a/patches/convex/util/compression.ts +++ b/patches/convex/util/compression.ts @@ -1,71 +1,71 @@ -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; -} +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; +} diff --git a/patches/convex/util/geometry.test.ts b/patches/convex/util/geometry.test.ts index 5c811a934a15d4c5c6b52ff0e33d98ecb09c3933..5e7e992ba93f7373d42488c387b9d63ab21e7bd6 100644 --- a/patches/convex/util/geometry.test.ts +++ b/patches/convex/util/geometry.test.ts @@ -1,298 +1,298 @@ -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], - ]); - }); -}); +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], + ]); + }); +}); diff --git a/patches/convex/util/geometry.ts b/patches/convex/util/geometry.ts index 4f9f3923014a10580667d560f774a7a94333c051..de89f2ca7ad8d7b1066a36894126dc62f28bc04e 100644 --- a/patches/convex/util/geometry.ts +++ b/patches/convex/util/geometry.ts @@ -1,132 +1,132 @@ -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; -} +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; +} diff --git a/patches/convex/util/isSimpleObject.ts b/patches/convex/util/isSimpleObject.ts index 20e993225184fa8ad6886aab2acf0cfdd28fe611..1dd83799b54928466eb7b342e40b96543eac8395 100644 --- a/patches/convex/util/isSimpleObject.ts +++ b/patches/convex/util/isSimpleObject.ts @@ -1,11 +1,11 @@ -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; -} +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; +} diff --git a/patches/convex/util/llm.ts b/patches/convex/util/llm.ts index b82d77fd4a364267614bd5bfc9a2b90a097b1256..a312bd4f9a618f50e1891f9cd8f47cf3cc1bbcf5 100644 --- a/patches/convex/util/llm.ts +++ b/patches/convex/util/llm.ts @@ -1,657 +1,662 @@ -import { HfInference } from "@huggingface/inference"; - -export const LLM_CONFIG = { - /* Hugginface config: */ - ollama: false, - huggingface: true, - url: "https://api-inference.huggingface.co/models/meta-llama/Meta-Llama-3-8B-Instruct", - chatModel: "meta-llama/Meta-Llama-3-8B-Instruct", - embeddingModel: - "https://api-inference.huggingface.co/models/mixedbread-ai/mxbai-embed-large-v1", - embeddingDimension: 1024, - - /* Ollama (local) config: - */ - // ollama: true, - // url: 'http://127.0.0.1:11434', - // chatModel: 'llama3' as const, - // embeddingModel: 'mxbai-embed-large', - // embeddingDimension: 1024, - // embeddingModel: 'llama3', - // embeddingDimension: 4096, - - /* Together.ai config: - ollama: false, - url: 'https://api.together.xyz', - chatModel: 'meta-llama/Llama-3-8b-chat-hf', - embeddingModel: 'togethercomputer/m2-bert-80M-8k-retrieval', - embeddingDimension: 768, - */ - - /* OpenAI config: - ollama: false, - url: 'https://api.openai.com', - chatModel: 'gpt-3.5-turbo-16k', - embeddingModel: 'text-embedding-ada-002', - embeddingDimension: 1536, - */ -}; - -function apiUrl(path: string) { - // OPENAI_API_BASE and OLLAMA_HOST are legacy - const host = - process.env.LLM_API_URL ?? - process.env.OLLAMA_HOST ?? - process.env.OPENAI_API_BASE ?? - LLM_CONFIG.url; - if (host.endsWith("/") && path.startsWith("/")) { - return host + path.slice(1); - } else if (!host.endsWith("/") && !path.startsWith("/")) { - return host + "/" + path; - } else { - return host + path; - } -} - -function apiKey() { - return process.env.LLM_API_KEY ?? process.env.OPENAI_API_KEY; -} - -const AuthHeaders = (): Record => - apiKey() - ? { - Authorization: "Bearer " + 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"]; - } -) { - assertApiKey(); - // OLLAMA_MODEL is legacy - body.model = - body.model ?? - process.env.LLM_MODEL ?? - process.env.OLLAMA_MODEL ?? - LLM_CONFIG.chatModel; - const stopWords = body.stop - ? typeof body.stop === "string" - ? [body.stop] - : body.stop - : []; - if (LLM_CONFIG.ollama || LLM_CONFIG.huggingface) stopWords.push("<|eot_id|>"); - - const { - result: content, - retries, - ms, - } = await retryWithBackoff(async () => { - const hf = new HfInference(apiKey()); - const model = hf.endpoint(apiUrl("/v1/chat/completions")); - if (body.stream) { - const completion = model.chatCompletionStream({ - ...body, - }); - return new ChatCompletionContent(completion, stopWords); - } else { - const completion = await model.chatCompletion({ - ...body, - }); - const content = completion.choices[0].message?.content; - if (content === undefined) { - throw new Error( - "Unexpected result from OpenAI: " + JSON.stringify(completion) - ); - } - 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(apiUrl("/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[]) { - if (LLM_CONFIG.ollama) { - return { - ollama: true as const, - embeddings: await Promise.all( - texts.map(async (t) => (await ollamaFetchEmbedding(t)).embedding) - ), - }; - } - assertApiKey(); - - if (LLM_CONFIG.huggingface) { - const result = await fetch(LLM_CONFIG.embeddingModel, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Wait-For-Model": "true", - ...AuthHeaders(), - }, - body: JSON.stringify({ - inputs: texts.map((text) => text.replace(/\n/g, " ")), - }), - }); - const embeddings = await result.json(); - return { - ollama: true as const, - embeddings: embeddings, - }; - } - - const { - result: json, - retries, - ms, - } = await retryWithBackoff(async () => { - const result = await fetch(apiUrl("/v1/embeddings"), { - method: "POST", - headers: { - "Content-Type": "application/json", - ...AuthHeaders(), - }, - - body: JSON.stringify({ - model: LLM_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) { - assertApiKey(); - const { result: flagged } = await retryWithBackoff(async () => { - const result = await fetch(apiUrl("/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; -} - -export function assertApiKey() { - if (!LLM_CONFIG.ollama && !apiKey()) { - throw new Error( - "\n Missing LLM_API_KEY in environment variables.\n\n" + - (LLM_CONFIG.ollama ? "just" : "npx") + - " convex env set LLM_API_KEY 'your-key'" - ); - } -} - -// 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' - // | 'gpt-3.5-turbo-0613' - // | 'gpt-3.5-turbo-16k' // <- our default - // | 'gpt-3.5-turbo-16k-0613'; - /** - * 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 completion: AsyncIterable; - private readonly stopWords: string[]; - - constructor( - completion: AsyncIterable, - stopWords: string[] - ) { - this.completion = completion; - this.stopWords = stopWords; - } - - async *readInner() { - for await (const chunk of this.completion) { - yield chunk.choices[0].delta.content; - } - } - - // 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; - } -} - -export async function ollamaFetchEmbedding(text: string) { - const { result } = await retryWithBackoff(async () => { - const resp = await fetch(apiUrl("/api/embeddings"), { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ model: LLM_CONFIG.embeddingModel, prompt: text }), - }); - if (resp.status === 404) { - const error = await resp.text(); - await tryPullOllama(LLM_CONFIG.embeddingModel, error); - throw new Error(`Failed to fetch embeddings: ${resp.status}`); - } - return (await resp.json()).embedding as number[]; - }); - return { embedding: result }; -} +// That's right! No imports and no dependencies 🤯 + +export const LLM_CONFIG = { + /* Ollama (local) config: + */ + ollama: true, + url: 'http://127.0.0.1:11434', + chatModel: 'llama3' as const, + embeddingModel: 'mxbai-embed-large', + embeddingDimension: 1024, + stopWords: ['<|eot_id|>'], + // embeddingModel: 'llama3', + // embeddingDimension: 4096, + + /* Together.ai config: + ollama: false, + url: 'https://api.together.xyz', + chatModel: 'meta-llama/Llama-3-8b-chat-hf', + embeddingModel: 'togethercomputer/m2-bert-80M-8k-retrieval', + embeddingDimension: 768, + stopWords: ['<|eot_id|>'], + */ + + /* OpenAI config: + ollama: false, + url: 'https://api.openai.com', + chatModel: 'gpt-3.5-turbo-16k', + embeddingModel: 'text-embedding-ada-002', + embeddingDimension: 1536, + */ +}; + +function apiUrl(path: string) { + // OPENAI_API_BASE and OLLAMA_HOST are legacy + const host = + process.env.LLM_API_URL ?? + process.env.OLLAMA_HOST ?? + process.env.OPENAI_API_BASE ?? + LLM_CONFIG.url; + if (host.endsWith('/') && path.startsWith('/')) { + return host + path.slice(1); + } else if (!host.endsWith('/') && !path.startsWith('/')) { + return host + '/' + path; + } else { + return host + path; + } +} + +function apiKey() { + return process.env.LLM_API_KEY ?? process.env.OPENAI_API_KEY; +} + +const AuthHeaders = (): Record => + apiKey() + ? { + Authorization: 'Bearer ' + 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']; + }, +) { + assertApiKey(); + // OLLAMA_MODEL is legacy + body.model = + body.model ?? process.env.LLM_MODEL ?? process.env.OLLAMA_MODEL ?? LLM_CONFIG.chatModel; + const stopWords = body.stop ? (typeof body.stop === 'string' ? [body.stop] : body.stop) : []; + if (LLM_CONFIG.stopWords) stopWords.push(...LLM_CONFIG.stopWords); + console.log(body); + const { + result: content, + retries, + ms, + } = await retryWithBackoff(async () => { + const result = await fetch(apiUrl('/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 && LLM_CONFIG.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(apiUrl('/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[]) { + if (LLM_CONFIG.ollama) { + return { + ollama: true as const, + embeddings: await Promise.all( + texts.map(async (t) => (await ollamaFetchEmbedding(t)).embedding), + ), + }; + } + assertApiKey(); + const { + result: json, + retries, + ms, + } = await retryWithBackoff(async () => { + const result = await fetch(apiUrl('/v1/embeddings'), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...AuthHeaders(), + }, + + body: JSON.stringify({ + model: LLM_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) { + assertApiKey(); + const { result: flagged } = await retryWithBackoff(async () => { + const result = await fetch(apiUrl('/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; +} + +export function assertApiKey() { + if (!LLM_CONFIG.ollama && !apiKey()) { + throw new Error( + '\n Missing LLM_API_KEY in environment variables.\n\n' + + (LLM_CONFIG.ollama ? 'just' : 'npx') + + " convex env set LLM_API_KEY 'your-key'", + ); + } +} + +// 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' + // | 'gpt-3.5-turbo-0613' + // | 'gpt-3.5-turbo-16k' // <- our default + // | 'gpt-3.5-turbo-16k-0613'; + /** + * 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 { result } = await retryWithBackoff(async () => { + const resp = await fetch(apiUrl('/api/embeddings'), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ model: LLM_CONFIG.embeddingModel, prompt: text }), + }); + if (resp.status === 404) { + const error = await resp.text(); + await tryPullOllama(LLM_CONFIG.embeddingModel, error); + throw new Error(`Failed to fetch embeddings: ${resp.status}`); + } + return (await resp.json()).embedding as number[]; + }); + return { embedding: result }; +} diff --git a/patches/convex/util/minheap.test.ts b/patches/convex/util/minheap.test.ts index 6bc6064d2cb63e513899192509d179350a8bd944..8cbea5e67a7ed34067161377a654d860d3a07182 100644 --- a/patches/convex/util/minheap.test.ts +++ b/patches/convex/util/minheap.test.ts @@ -1,62 +1,62 @@ -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'); - }); -}); +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'); + }); +}); diff --git a/patches/convex/util/minheap.ts b/patches/convex/util/minheap.ts index 8f83b945aa7cfb8d6a4f6a6d54591651da5128f3..64ba5b0b5748c8ae78c53c5b2f9e87aaedb59a40 100644 --- a/patches/convex/util/minheap.ts +++ b/patches/convex/util/minheap.ts @@ -1,38 +1,38 @@ -// 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; - }, - }; -} +// 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; + }, + }; +} diff --git a/patches/convex/util/object.ts b/patches/convex/util/object.ts index acc07b68ca0bfe22d4c5e543e8026d8ae7870dea..9c3745c88205fd3f79c125eea00296ad911b7ba3 100644 --- a/patches/convex/util/object.ts +++ b/patches/convex/util/object.ts @@ -1,22 +1,22 @@ -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()); -} +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()); +} diff --git a/patches/convex/util/sleep.ts b/patches/convex/util/sleep.ts index 5a311c590889d6594625d98a9243639918ddcae5..69dc25d672b5814fc6b893208462b3af99b100f2 100644 --- a/patches/convex/util/sleep.ts +++ b/patches/convex/util/sleep.ts @@ -1,3 +1,3 @@ -export async function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} +export async function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/patches/convex/util/types.test.ts b/patches/convex/util/types.test.ts index a4ba8dab6e4d22856df89880837b7e8ec0c67985..537ec384e2fa24f20194151f9623e3fe6f883b8c 100644 --- a/patches/convex/util/types.test.ts +++ b/patches/convex/util/types.test.ts @@ -1,42 +1,42 @@ -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); - }); +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); + }); }); \ No newline at end of file diff --git a/patches/convex/util/types.ts b/patches/convex/util/types.ts index 6a33fb3636274206fbf6267594ef8a8ab0c99bbc..c44a8c8271a408181ea19bbe5292516a628e77ad 100644 --- a/patches/convex/util/types.ts +++ b/patches/convex/util/types.ts @@ -1,33 +1,33 @@ -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], - }; -} +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], + }; +} diff --git a/patches/convex/util/xxhash.ts b/patches/convex/util/xxhash.ts index aa6cd95cffc7af21b1cef2d8e7b1d131eae49509..7b9e73e7474b55f99a947395aafa34cdce27c68c 100644 --- a/patches/convex/util/xxhash.ts +++ b/patches/convex/util/xxhash.ts @@ -1,228 +1,228 @@ -/* -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; -} +/* +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; +} diff --git a/patches/convex/world.ts b/patches/convex/world.ts index f70d307df26f0b68d95dd979b396af08b572e88a..69b71f6ee882e9e56c0dd413bbfa4880214c8d38 100644 --- a/patches/convex/world.ts +++ b/patches/convex/world.ts @@ -1,274 +1,271 @@ -import { ConvexError, v } from 'convex/values'; -import { internalMutation, mutation, query } from './_generated/server'; -import { characters } from '../data/characters'; -import { Descriptions } 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'), - oauthToken: v.optional(v.string()), - - }, - handler: async (ctx, args) => { - const { worldId, oauthToken } = args; - - if (!oauthToken) { - return null; - } - - return oauthToken; - }, -}); - -export const joinWorld = mutation({ - args: { - worldId: v.id('worlds'), - oauthToken: v.optional(v.string()), - - }, - handler: async (ctx, args) => { - const { worldId, oauthToken } = args; - - if (!oauthToken) { - throw new ConvexError(`Not logged in`); - } - // if (!identity) { - // throw new ConvexError(`Not logged in`); - // } - // const name = - // identity.givenName || identity.nickname || (identity.email && identity.email.split('@')[0]); - const name = oauthToken; - - // 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}`); - } - // Select a random character description - const randomCharacter = Descriptions[Math.floor(Math.random() * Descriptions.length)]; - - return await insertInput(ctx, world._id, 'join', { - name: randomCharacter.name, - character: randomCharacter.character, - description: randomCharacter.identity, - tokenIdentifier: oauthToken, - role: "villager", - }); - }, - }); - - -export const leaveWorld = mutation({ - args: { - worldId: v.id('worlds'), - oauthToken: v.optional(v.string()), - }, - handler: async (ctx, args) => { - const { worldId, oauthToken } = args; - - - console.log('OAuth Name:', oauthToken); - if (!oauthToken) { - throw new ConvexError(`Not logged in`); - } - - 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 === oauthToken); - 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; - }, -}); +import { ConvexError, v } from 'convex/values'; +import { internalMutation, mutation, query } from './_generated/server'; +import { characters } from '../data/characters'; +import { Descriptions } 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'), + oauthToken: v.optional(v.string()), + }, + handler: async (ctx, args) => { + const { worldId, oauthToken } = args; + + if (!oauthToken) { + return null; + } + + return oauthToken; + }, +}); + +export const joinWorld = mutation({ + args: { + worldId: v.id('worlds'), + oauthToken: v.optional(v.string()), + }, + handler: async (ctx, args) => { + const { worldId, oauthToken } = args; + + if (!oauthToken) { + throw new ConvexError(`Not logged in`); + } + // if (!identity) { + // throw new ConvexError(`Not logged in`); + // } + // const name = + // identity.givenName || identity.nickname || (identity.email && identity.email.split('@')[0]); + const name = oauthToken; + + // 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}`); + } + // Select a random character description + const randomCharacter = Descriptions[Math.floor(Math.random() * Descriptions.length)]; + // const { tokenIdentifier } = identity; + return await insertInput(ctx, world._id, 'join', { + name: randomCharacter.name, + character: randomCharacter.character, + description: randomCharacter.identity, + tokenIdentifier: oauthToken, + // By default everybody is a villager + type: 'villager', + }); + }, +}); + +export const leaveWorld = mutation({ + args: { + worldId: v.id('worlds'), + oauthToken: v.optional(v.string()), + }, + handler: async (ctx, args) => { + const { worldId, oauthToken } = args; + + + console.log('OAuth Name:', oauthToken); + if (!oauthToken) { + throw new ConvexError(`Not logged in`); + } + + 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 === oauthToken); + 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; + }, +}); diff --git a/patches/src/App.tsx b/patches/src/App.tsx index 4a033f508e4b8333a7890dc50d74aa6b0ad033f2..75bae852aedb73a0b06e690b583fc7a0bc5d2f85 100644 --- a/patches/src/App.tsx +++ b/patches/src/App.tsx @@ -1,98 +1,121 @@ -import Game from './components/Game.tsx'; - -import { ToastContainer } from 'react-toastify'; - -// 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 InteractButton from './components/buttons/InteractButton.tsx'; -import OAuthLogin from './components//buttons/OAuthLogin.tsx'; -import { MAX_HUMAN_PLAYERS } from '../convex/constants.ts'; - -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. -

-
-
- -
- - -
-
- - - - -
-
- -
-
- ); -} - -const modalStyles = { - overlay: { - backgroundColor: 'rgb(0, 0, 0, 75%)', - zIndex: 0, - }, - content: { - top: '800%', - left: '80%', - right: 'auto', - bottom: 'auto', - marginRight: '-50%', - transform: 'translate(-50%, -50%)', - maxWidth: '90%', - - border: '1px solid rgb(23, 20, 33)', - borderRadius: '0', - background: 'rgb(35, 38, 58)', - color: 'white', - fontFamily: '"Upheaval Pro", "sans-serif"', - }, -}; +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 OAuthLogin from './components//buttons/OAuthLogin.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. +

+
+
+ {/*
+ + + + + + + +
*/} + +
+ + +
+
+ + + + + + +
+ +
+ +
+
+ ); +} + + +const modalStyles = { + overlay: { + backgroundColor: 'rgb(0, 0, 0, 75%)', + zIndex: 0, + }, + content: { + top: '800%', + left: '80%', + right: 'auto', + bottom: 'auto', + marginRight: '-50%', + transform: 'translate(-50%, -50%)', + maxWidth: '90%', + + border: '1px solid rgb(23, 20, 33)', + borderRadius: '0', + background: 'rgb(35, 38, 58)', + color: 'white', + fontFamily: '"Upheaval Pro", "sans-serif"', + }, +}; \ No newline at end of file diff --git a/patches/src/components/Character.tsx b/patches/src/components/Character.tsx index bbc60518038ad74a6d180a5e1f3932ee36720726..d6a5fd27ba04ebcb26824aae36cd36d75fbb1fa3 100644 --- a/patches/src/components/Character.tsx +++ b/patches/src/components/Character.tsx @@ -1,120 +1,120 @@ -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 ; -} +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 ; +} diff --git a/patches/src/components/ConvexClientProvider.tsx b/patches/src/components/ConvexClientProvider.tsx index 7f222816b64e961daa17947a4fbe0f8818c64081..4ba83e1f08f8e3cf87c8d8fe9ba37fc25f597490 100644 --- a/patches/src/components/ConvexClientProvider.tsx +++ b/patches/src/components/ConvexClientProvider.tsx @@ -1,30 +1,30 @@ -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} - // - // - ); -} +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} + // + // + ); +} diff --git a/patches/src/components/DebugPath.tsx b/patches/src/components/DebugPath.tsx index 605e268b8beb5e10917f52ddc329d02d01a3f897..2502a862d8b9681cf0af50653fdf0a3a6f24e148 100644 --- a/patches/src/components/DebugPath.tsx +++ b/patches/src/components/DebugPath.tsx @@ -1,36 +1,36 @@ -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 }; -} +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 }; +} diff --git a/patches/src/components/DebugTimeManager.tsx b/patches/src/components/DebugTimeManager.tsx index c9505ce2221784237251690b058ed4778fc04286..1b1f7267d7f65f000f9bd807a9db64923d37afcc 100644 --- a/patches/src/components/DebugTimeManager.tsx +++ b/patches/src/components/DebugTimeManager.tsx @@ -1,156 +1,156 @@ -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); +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); diff --git a/patches/src/components/FreezeButton.tsx b/patches/src/components/FreezeButton.tsx index 0c48bf04c367eee050430d47bee7b8d43aba550c..2c168f927b3d9ea233bf0e3cd525d441e0e321c4 100644 --- a/patches/src/components/FreezeButton.tsx +++ b/patches/src/components/FreezeButton.tsx @@ -1,36 +1,36 @@ -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 : ( - <> - - - ); -} +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 : ( + <> + + + ); +} diff --git a/patches/src/components/Game.tsx b/patches/src/components/Game.tsx index 926b81167eeda86a35c5e3558c9ec8321edf20a6..4321ee6cc3ee95e3cb416fa23c1a6e2cb6554cd0 100644 --- a/patches/src/components/Game.tsx +++ b/patches/src/components/Game.tsx @@ -1,85 +1,126 @@ -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 */} -
- -
-
- - ); -} +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'; +import { VoteModal } from './VoteModal.tsx'; +import { GameCycle } from '../../convex/aiTown/gameCycle.ts'; +import { PlayerDescription } from '../../convex/aiTown/playerDescription.ts'; + +export const SHOW_DEBUG_UI = !!import.meta.env.VITE_SHOW_DEBUG_UI; + +export function VotingName(gameState: string) { + switch (gameState) { + case 'warewolf-vote': + return { + name: 'Warewolf Vote', + desc: 'Select a player who is warewolf', + type: 'warewolf-vote', + }; + case 'player-kill': + return { + name: 'Player Kill', + desc: 'Select a player to kill', + type: 'player-kill', + }; + default: + return { + name: 'Voting', + desc: 'Select a player to vote', + type: 'voting', + }; + } +} + +export function isVotingState(gameCycle: GameCycle) { + return gameCycle.cycleIndex === 0 || gameCycle.cycleIndex === 2; +} + +function showMap(gameCycle: GameCycle, player: PlayerDescription) { + // Here also check for player description + return gameCycle.cycleIndex === 2 || gameCycle.cycleIndex == 1; +} + + +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); + // TODO: base this on the game state + const [gameState, setGameState] = useState<'warewolf-vote' | 'player-kill' | 'none'>('none'); + + 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 */} +
+ {isVotingState(game.world.gameCycle) ? : + +} +
+
+ + ); +} diff --git a/patches/src/components/MessageInput.tsx b/patches/src/components/MessageInput.tsx index 79cef42d538d8a1604d2a5f8797b7fbfa6e720a3..8e611edf00dacae4845efe85a139e369ab14478d 100644 --- a/patches/src/components/MessageInput.tsx +++ b/patches/src/components/MessageInput.tsx @@ -1,94 +1,94 @@ -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)} - /> -

-
- ); -} +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)} + /> +

+
+ ); +} diff --git a/patches/src/components/Messages.tsx b/patches/src/components/Messages.tsx index 52d52f553339a2cf5ceca4ea42b136e271d4c1f3..66995ee3516f466c2767eafc3cc6dcf905bb594f 100644 --- a/patches/src/components/Messages.tsx +++ b/patches/src/components/Messages.tsx @@ -1,167 +1,167 @@ -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' && ( - - )} -
-
- ); -} +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' && ( + + )} +
+
+ ); +} diff --git a/patches/src/components/PixiGame.tsx b/patches/src/components/PixiGame.tsx index afa330ebe3c0fc318f7f8e6fc5cb0a5748d6a7b1..85542897eb9d8f3512697dcbf78e3542a8cc3210 100644 --- a/patches/src/components/PixiGame.tsx +++ b/patches/src/components/PixiGame.tsx @@ -1,160 +1,162 @@ -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 oauth = JSON.parse(localStorage.getItem('oauth')); - const oauthToken = oauth ? oauth.userInfo.fullname : undefined; - const humanTokenIdentifier = useQuery(api.world.userStatus, { worldId: props.worldId, oauthToken }) ?? 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()]; - - // State to manage the current tileSet - const [currentTileSet, setCurrentTileSet] = useState({ - background: props.game.worldMap.bgTiles, - objectMap: props.game.worldMap.objectTiles, - decor: props.game.worldMap.decorTiles, - }); - - // Effect hook to change the tileSet based on a day/night condition - useEffect(() => { - const tileSet = props.game.world.dayNightCycle.isDay - ? { - background: props.game.worldMap.bgTiles, - objectMap: props.game.worldMap.objectTiles, - decor: props.game.worldMap.decorTiles, - } : { - background: props.game.worldMap.bgTilesN, - objectMap: props.game.worldMap.objectTilesN, - decor: props.game.worldMap.decorTilesN, - }; - setCurrentTileSet(tileSet); - }, [props.game.world.dayNightCycle.isDay]); - // 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; +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 oauth = JSON.parse(localStorage.getItem('oauth')); + const oauthToken = oauth ? oauth.userInfo.fullname : undefined; + const humanTokenIdentifier = useQuery(api.world.userStatus, { worldId: props.worldId, oauthToken }) ?? 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()]; + + // State to manage the current tileSet + const [currentTileSet, setCurrentTileSet] = useState({ + background: props.game.worldMap.bgTiles, + objectMap: props.game.worldMap.objectTiles, + decor: props.game.worldMap.decorTiles, + }); + + // Effect hook to change the tileSet based on a day/night condition + useEffect(() => { + const { cycleState } = props.game.world.gameCycle; + const tileSet = (cycleState === 'Day' || cycleState === 'Night') + ? { + background: props.game.worldMap.bgTiles, + objectMap: props.game.worldMap.objectTiles, + decor: props.game.worldMap.decorTiles, + } + : { + background: props.game.worldMap.bgTilesN, + objectMap: props.game.worldMap.objectTilesN, + decor: props.game.worldMap.decorTilesN, + }; + setCurrentTileSet(tileSet); + }, [props.game.world.gameCycle.cycleState]); + + // 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; diff --git a/patches/src/components/PixiStaticMap.tsx b/patches/src/components/PixiStaticMap.tsx index 02121f9251791f48c33c19b4e6f14f59be9b9336..57c33821fd089756b54f5941fac24c002a559e96 100644 --- a/patches/src/components/PixiStaticMap.tsx +++ b/patches/src/components/PixiStaticMap.tsx @@ -1,166 +1,166 @@ -import { PixiComponent, applyDefaultProps } from '@pixi/react'; -import * as PIXI from 'pixi.js'; -import { AnimatedSprite, WorldMap, TileLayer } 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'; -import React from 'react'; - -export type MapProps = { - map: WorldMap; - [k: string]: any; -}; - -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',}, -}; - -const createTiles = (map: WorldMap) => { - 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), - ); - } - } - return tiles; -}; - -const renderMap = (container: PIXI.Container, map:WorldMap, tiles: PIXI.Texture[]) => { - const screenxtiles = map.bgTiles[0].length; - const screenytiles = map.bgTiles[0][0].length; - - const allLayers = [...map.bgTiles, ...map.objectTiles, ...map.decorTiles]; - - // 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); - } - } -}; - -const createAnimatedSprites = (container: PIXI.Container, map: WorldMap) => { - 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(); - } - }); - } -}; - -export const PixiStaticMapComponent = PixiComponent('StaticMap', { - create: (props: { map: WorldMap; [k: string]: any }) => { - const container = new PIXI.Container(); - const tiles = createTiles(props.map); - renderMap(container, props.map, tiles); - createAnimatedSprites(container, props.map); - - container.x = 0; - container.y = 0; - - // Set the hit area manually to ensure `pointerdown` events are delivered to this container. - const screenxtiles = props.map.bgTiles[0].length; - const screenytiles = props.map.bgTiles[0][0].length; - container.interactive = true; - container.hitArea = new PIXI.Rectangle( - 0, - 0, - screenxtiles * props.map.tileDim, - screenytiles * props.map.tileDim, - ); - - return container; - }, - - applyProps: (instance, oldProps: any, newProps: any) => { - if (oldProps.map !== newProps.map) { - instance.removeChildren(); - const tiles = createTiles(newProps.map); - renderMap(instance, newProps.map, tiles); - createAnimatedSprites(instance, newProps.map); - } - - applyDefaultProps(instance, oldProps, newProps); - }, -}); - -const PixiStaticMap = React.memo( - (props: MapProps) => { - return ; - }, - (prevProps, nextProps) => { - return ( - prevProps.map.bgTiles === nextProps.map.bgTiles && - prevProps.map.objectTiles === nextProps.map.objectTiles - ); - } -); - -export default PixiStaticMap; +import { PixiComponent, applyDefaultProps } from '@pixi/react'; +import * as PIXI from 'pixi.js'; +import { AnimatedSprite, WorldMap, TileLayer } 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'; +import React from 'react'; + +export type MapProps = { + map: WorldMap; + [k: string]: any; +}; + +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',}, +}; + +const createTiles = (map: WorldMap) => { + 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), + ); + } + } + return tiles; +}; + +const renderMap = (container: PIXI.Container, map:WorldMap, tiles: PIXI.Texture[]) => { + const screenxtiles = map.bgTiles[0].length; + const screenytiles = map.bgTiles[0][0].length; + + const allLayers = [...map.bgTiles, ...map.objectTiles, ...map.decorTiles]; + + // 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); + } + } +}; + +const createAnimatedSprites = (container: PIXI.Container, map: WorldMap) => { + 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(); + } + }); + } +}; + +export const PixiStaticMapComponent = PixiComponent('StaticMap', { + create: (props: { map: WorldMap; [k: string]: any }) => { + const container = new PIXI.Container(); + const tiles = createTiles(props.map); + renderMap(container, props.map, tiles); + createAnimatedSprites(container, props.map); + + container.x = 0; + container.y = 0; + + // Set the hit area manually to ensure `pointerdown` events are delivered to this container. + const screenxtiles = props.map.bgTiles[0].length; + const screenytiles = props.map.bgTiles[0][0].length; + container.interactive = true; + container.hitArea = new PIXI.Rectangle( + 0, + 0, + screenxtiles * props.map.tileDim, + screenytiles * props.map.tileDim, + ); + + return container; + }, + + applyProps: (instance, oldProps: any, newProps: any) => { + if (oldProps.map !== newProps.map) { + instance.removeChildren(); + const tiles = createTiles(newProps.map); + renderMap(instance, newProps.map, tiles); + createAnimatedSprites(instance, newProps.map); + } + + applyDefaultProps(instance, oldProps, newProps); + }, +}); + +const PixiStaticMap = React.memo( + (props: MapProps) => { + return ; + }, + (prevProps, nextProps) => { + return ( + prevProps.map.bgTiles === nextProps.map.bgTiles && + prevProps.map.objectTiles === nextProps.map.objectTiles + ); + } +); + +export default PixiStaticMap; diff --git a/patches/src/components/PixiViewport.tsx b/patches/src/components/PixiViewport.tsx index 395da46233a1741bc15f710f6db1eff7bf16d74d..d32715e848d8dc787e55295f885630f505e0b260 100644 --- a/patches/src/components/PixiViewport.tsx +++ b/patches/src/components/PixiViewport.tsx @@ -1,56 +1,56 @@ -// 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]; - } - }); - }, -}); +// 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]; + } + }); + }, +}); diff --git a/patches/src/components/Player.tsx b/patches/src/components/Player.tsx index 477863cb7f3888245902fa891e9d778cc81c15a7..08b807e1250c0abca37f66ef63514a75f3513525 100644 --- a/patches/src/components/Player.tsx +++ b/patches/src/components/Player.tsx @@ -1,91 +1,91 @@ -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 }); - }} - /> - - ); -}; +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 }); + }} + /> + + ); +}; diff --git a/patches/src/components/PlayerDetails.tsx b/patches/src/components/PlayerDetails.tsx index db0418a82b019c36e65d3b0e6dcad32dec48b394..e8559af07009d503143b6046b9eb2b1bbdcdd43f 100644 --- a/patches/src/components/PlayerDetails.tsx +++ b/patches/src/components/PlayerDetails.tsx @@ -1,268 +1,265 @@ -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 oauth = JSON.parse(localStorage.getItem('oauth')); - const oauthToken = oauth ? oauth.userInfo.fullname : undefined; - const humanTokenIdentifier = useQuery(api.world.userStatus, { worldId, oauthToken }); - - - 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); - console.log("playerId",playerId) - console.log('player',player) - console.log('playerDescription',playerDescription) - 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 si you ! You are a } - {!isMe && inConversationWithMe && ( - <> -
-
(Conversing with you!) - - )} -

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

Previous conversation

-
- - - )} - - ); -} +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 oauth = JSON.parse(localStorage.getItem('oauth')); + const oauthToken = oauth ? oauth.userInfo.fullname : undefined; + const humanTokenIdentifier = useQuery(api.world.userStatus, { worldId, oauthToken }); + + 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

+
+ + + )} + + ); +} diff --git a/patches/src/components/PositionIndicator.tsx b/patches/src/components/PositionIndicator.tsx index dd0c8008258d1571c839840f81734dfd6826035e..5ad7dd01cfa621e126b034323275c4bcdfe7b4ab 100644 --- a/patches/src/components/PositionIndicator.tsx +++ b/patches/src/components/PositionIndicator.tsx @@ -1,26 +1,26 @@ -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 ; -} +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 ; +} diff --git a/patches/src/components/PoweredByConvex.tsx b/patches/src/components/PoweredByConvex.tsx index 195c062186af6d1b5eb1956a1dc6d9486742bca4..9458e1b74afa67a6cc2ae26ac26d2abb121a37a4 100644 --- a/patches/src/components/PoweredByConvex.tsx +++ b/patches/src/components/PoweredByConvex.tsx @@ -1,50 +1,50 @@ -import bannerBg from '../../assets/convex-bg.webp'; -export default function PoweredByConvex() { - return ( - - - -
- -
-
- - Powered by - - - Convex - - - - - - - - - - - - - - - -
-
-
- ); -} +import bannerBg from '../../assets/convex-bg.webp'; +export default function PoweredByConvex() { + return ( + + + +
+ +
+
+ + Powered by + + + Convex + + + + + + + + + + + + + + + +
+
+
+ ); +} diff --git a/patches/src/components/VoteModal.tsx b/patches/src/components/VoteModal.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e78cc81a733201d3d261da61f48e119d89952a3c --- /dev/null +++ b/patches/src/components/VoteModal.tsx @@ -0,0 +1,113 @@ +import { ServerGame } from "@/hooks/serverGame"; +import { SelectElement } from "./Player"; +import { GameId } from "../../convex/aiTown/ids"; +import Button from "./buttons/Button"; +import { useQuery } from "convex/react"; +import { api } from "../../convex/_generated/api"; +import { Id } from '../../convex/_generated/dataModel'; +import { characters } from "../../data/characters"; +import { useEffect, useState } from "react"; +import { BaseTexture, SCALE_MODES, Spritesheet } from "pixi.js"; +import { Sprite, Stage } from "@pixi/react"; +import { Character } from "./Character"; +import { useSendInput } from "../hooks/sendInput"; +import { GameCycle } from "../../convex/aiTown/gameCycle"; +export type Vote = (id: GameId<'players'>) => void; + +export function VotingName(gameCycle: GameCycle) { + switch (gameCycle.cycleIndex) { + case 2: + return { + name: 'Warewolf Vote', + desc: 'Select a player who is warewolf', + type: 'WarewolfVote', + }; + case 3: + return { + name: 'Player Kill', + desc: 'Select a player to kill', + type: 'PlayerKill', + }; + default: + return { + name: 'Error', + desc: 'Select a player to vote', + type: 'Error' + }; + } +} + +export const VoteModal = ({ + worldId, + engineId, + game, +}: { + worldId: Id<'worlds'>, + engineId: Id<'engines'>, + game: ServerGame, + +}) => { + const [hasVoted, setHasVoted] = useState(false); + const vote = useSendInput(engineId, 'vote'); + const onVote = (playerId: GameId<'players'>) => { + if (hasVoted) return; + vote({votedPlayerId: playerId, voteType: gameState}); + setHasVoted(true); + } + const gameState = "warewolf-vote" + + + const humanTokenIdentifier = useQuery(api.world.userStatus, {worldId}); + const [spriteSheet, setSpriteSheet] = useState(); + const character = characters[0] + useEffect(() => { + const parseSheet = async () => { + const sheet = new Spritesheet( + BaseTexture.from(character.textureUrl, { + scaleMode: SCALE_MODES.NEAREST, + }), + character.spritesheetData, + ); + await sheet.parse(); + setSpriteSheet(sheet); + }; + void parseSheet(); + }, []); + // TODO only let people select non-dead players + const selectablePlayers = [...game.world.players.values()].filter( + (player) => player.id !== humanTokenIdentifier + ); + return ( + <> +
+

{VotingName(game.world.gameCycle).name}

+

{VotingName(game.world.gameCycle).desc}

+
+ {selectablePlayers.map((playable) => { + const playerDesc = game.playerDescriptions.get(playable.id); + const character = characters.find((c) => c.name === playerDesc?.character); + if (!character) return null; + return ( + <> + + + ) + })} + + ) + + + +} + diff --git a/patches/src/components/buttons/Button.tsx b/patches/src/components/buttons/Button.tsx index 7f85d18f52b0f1bc74674fda613fe7d50042fbb8..0103cf899cea41a9d8448f9ced1cf42f793eb5bb 100644 --- a/patches/src/components/buttons/Button.tsx +++ b/patches/src/components/buttons/Button.tsx @@ -1,33 +1,30 @@ -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} -
-
-
-
- ); -} +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.imgUrl && } + {props.children} +
+
+
+ ); +} diff --git a/patches/src/components/buttons/InteractButton.tsx b/patches/src/components/buttons/InteractButton.tsx index 2f9e450416a7e0474d06eca1b4dc952621e46b0e..14ca4cb80a63aa09aae2c2fce50927c8352de3e9 100644 --- a/patches/src/components/buttons/InteractButton.tsx +++ b/patches/src/components/buttons/InteractButton.tsx @@ -1,88 +1,88 @@ -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 oauth = JSON.parse(localStorage.getItem('oauth')); - const oauthToken = oauth ? oauth.userInfo.fullname : undefined; - console.log(oauthToken) - const humanTokenIdentifier = useQuery(api.world.userStatus, worldId ? { worldId, oauthToken } : '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, oauthToken }); - } 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, join, oauthToken], - ); - - - const joinOrLeaveGame = () => { - if ( - !worldId || - // || !isAuthenticated - game === undefined - ) { - return; - } - if (isPlaying) { - console.log(`Leaving game for player ${userPlayerId}`); - void leave({ worldId , oauthToken}); - } else { - console.log(`Joining game`); - void joinInput(worldId); - } - }; - // if (!isAuthenticated || game === undefined) { - // return ( - // - // - // - // ); - // } - return ( - - ); -} +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 oauth = JSON.parse(localStorage.getItem('oauth')); + const oauthToken = oauth ? oauth.userInfo.fullname : undefined; + console.log(oauthToken) + const humanTokenIdentifier = useQuery(api.world.userStatus, worldId ? { worldId, oauthToken } : '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, oauthToken }); + } 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, join, oauthToken], + ); + + + const joinOrLeaveGame = () => { + if ( + !worldId || + // || !isAuthenticated + game === undefined + ) { + return; + } + if (isPlaying) { + console.log(`Leaving game for player ${userPlayerId}`); + void leave({ worldId , oauthToken}); + } else { + console.log(`Joining game`); + void joinInput(worldId); + } + }; + // if (!isAuthenticated || game === undefined) { + // return ( + // + // + // + // ); + // } + return ( + + ); +} diff --git a/patches/src/components/buttons/LoginButton.tsx b/patches/src/components/buttons/LoginButton.tsx index 389cf220af1f89ba04956906e1c09a8daa651267..18260f01e0d8715c7259716fbd31477016f391d2 100644 --- a/patches/src/components/buttons/LoginButton.tsx +++ b/patches/src/components/buttons/LoginButton.tsx @@ -1,13 +1,13 @@ -import { SignInButton } from '@clerk/clerk-react'; - -export default function LoginButton() { - return ( - - - - ); -} +import { SignInButton } from '@clerk/clerk-react'; + +export default function LoginButton() { + return ( + + + + ); +} diff --git a/patches/src/components/buttons/MusicButton.tsx b/patches/src/components/buttons/MusicButton.tsx index f3a62a6218fa947018935d6b4d1be9aaccebfdba..427fd843e0876d84402a7d8819ff80b540fac789 100644 --- a/patches/src/components/buttons/MusicButton.tsx +++ b/patches/src/components/buttons/MusicButton.tsx @@ -1,53 +1,53 @@ -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 ( - <> - - - ); -} +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 ( + <> + + + ); +} diff --git a/patches/src/components/buttons/OAuthLogin.tsx b/patches/src/components/buttons/OAuthLogin.tsx deleted file mode 100644 index 19555f26c0f5273ec4d7fc5b13aab53284f63b58..0000000000000000000000000000000000000000 --- a/patches/src/components/buttons/OAuthLogin.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useState, useEffect } from 'react'; -import Button from './Button'; -import hf from '../../../assets/hf.svg'; -import { oauthLoginUrl, oauthHandleRedirectIfPresent } from '@huggingface/hub'; - -const OAuthLogin = () => { - const [isSignedIn, setIsSignedIn] = useState(false); - - useEffect(() => { - const checkAuthStatus = async () => { - let oauthResult = localStorage.getItem('oauth'); - if (oauthResult) { - try { - oauthResult = JSON.parse(oauthResult); - } catch { - oauthResult = null; - } - } - - if (!oauthResult) { - oauthResult = await oauthHandleRedirectIfPresent(); - if (oauthResult) { - localStorage.setItem('oauth', JSON.stringify(oauthResult)); - } - } - - setIsSignedIn(!!oauthResult); - }; - - checkAuthStatus(); - }, []); - - const handleSignIn = async () => { - let clientId = import.meta.env.VITE_OAUTH_CLIENT_ID; - window.location.href = await oauthLoginUrl({ clientId }); - }; - - const handleSignOut = () => { - localStorage.removeItem('oauth'); - window.location.href = window.location.href.replace(/\?.*$/, ''); - setIsSignedIn(false); - }; - - return ( -
- {isSignedIn ? ( -
- -
- ) : ( - - )} -
- ); -}; - -export default OAuthLogin; \ No newline at end of file diff --git a/patches/src/editor/campfire.json b/patches/src/editor/campfire.json index 27a70cf9581ac66e31cc2b7f399a60a2cd72201a..f62a05aa0e06a2353244aa017edbd84753e74f08 100644 --- a/patches/src/editor/campfire.json +++ b/patches/src/editor/campfire.json @@ -1,45 +1,45 @@ -{"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" -} -} +{"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" +} +} diff --git a/patches/src/editor/eutils.js b/patches/src/editor/eutils.js index c22a503d74b27bab748c87d6995814d8ab945a25..f3e7928833e4e9c31a22076041b0c30ff16f768a 100644 --- a/patches/src/editor/eutils.js +++ b/patches/src/editor/eutils.js @@ -1,19 +1,19 @@ - -// 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); - } + +// 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); + } } \ No newline at end of file diff --git a/patches/src/editor/gentlesparkle.json b/patches/src/editor/gentlesparkle.json index 8f4589b37ca4e709281eb59ea0f5c3ada6508bdb..3e90f341693bcf572059b2607e3a15a73077fb18 100644 --- a/patches/src/editor/gentlesparkle.json +++ b/patches/src/editor/gentlesparkle.json @@ -1,37 +1,37 @@ -{"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" -} +{"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" +} } \ No newline at end of file diff --git a/patches/src/editor/gentlesplash.json b/patches/src/editor/gentlesplash.json index 2d9c1895fe33879d7299ece6b7fd8fb73d6c26ad..a345e269f535cfaafb0b3ca299405542b049332d 100644 --- a/patches/src/editor/gentlesplash.json +++ b/patches/src/editor/gentlesplash.json @@ -1,61 +1,61 @@ -{"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" -} +{"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" +} } \ No newline at end of file diff --git a/patches/src/editor/gentlewaterfall.json b/patches/src/editor/gentlewaterfall.json index 814c0404f69d2b263b0ec46e9b6cbd1df81af4c9..8b0efcf37c7cdae0928dc19888db6ccf9bd7f8ff 100644 --- a/patches/src/editor/gentlewaterfall.json +++ b/patches/src/editor/gentlewaterfall.json @@ -1,61 +1,61 @@ -{"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" -} +{"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" +} } \ No newline at end of file diff --git a/patches/src/editor/index.html b/patches/src/editor/index.html index 309f06ec4ba1a1b0639bb4fa11f54f8dd4dba2ec..1d54313f50da5f33f16b9ec0a7164371d01481d8 100644 --- a/patches/src/editor/index.html +++ b/patches/src/editor/index.html @@ -1,7 +1,7 @@ - - - -Level editer
-Sprite editer - - + + + +Level editer
+Sprite editer + + diff --git a/patches/src/editor/le.html b/patches/src/editor/le.html index d85144f16e4a1c7f73547f0adbfa8053f2fd6fea..504703f8133c7d38460232ba8a40fbf145a63ef3 100644 --- a/patches/src/editor/le.html +++ b/patches/src/editor/le.html @@ -1,99 +1,99 @@ - - - - - - - - -
- - - -
- -
- - - - - - - - - - - - - - -
-
-
-
-
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - -
Composite to pngLoad levelLoad png to CompositeLoad Sprite
- - - - - - - - -
- 16 - 32 -
-
Load Tileset
-
- - - + + + + + + + + +
+ + + +
+ +
+ + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + +
Composite to pngLoad levelLoad png to CompositeLoad Sprite
+ + + + + + + + +
+ 16 + 32 +
+
Load Tileset
+
+ + + \ No newline at end of file diff --git a/patches/src/editor/le.js b/patches/src/editor/le.js index 17ca68df167fcb89815ebdaad1a61a2cb7ac74ed..838f0a1f499d9c30125980e27a6f41f30825b49d 100644 --- a/patches/src/editor/le.js +++ b/patches/src/editor/le.js @@ -1,1327 +1,1327 @@ -// -- -// 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]) - } - 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; - 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 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 ti = layer.addTileLevelPx(e.data.global.x, e.data.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.tiledimx, index[2]); - UNDO.undo_add_index_to_task(ti); - } - 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 squareindex = (j * g_ctx.tilesettilew) + i; - let ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, 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.tiledimx, selected_grid[0][0][2]); - } - else if (i == endtilex) { // top right corner - ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[column - 1][0][2]); - } else { // top middle - ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, 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.tiledimx, selected_grid[0][row][2]); - } - else if (i == endtilex) { // bottom right corner - ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[column - 1][row][2]); - } else { // bottom middle - ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[1][row][2]); - } - } else { // middle row - if (i === starttilex) { // middle left - ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[0][(row > 0)? 1 : 0][2]); - } - else if (i === endtilex) { // middle end - ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[column - 1][(row > 0)? 1 : 0][2]); - } else { // middle middle - ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, 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: 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)); -} - +// -- +// 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]) + } + 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; + 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 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 ti = layer.addTileLevelPx(e.data.global.x, e.data.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.tiledimx, index[2]); + UNDO.undo_add_index_to_task(ti); + } + 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 squareindex = (j * g_ctx.tilesettilew) + i; + let ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, 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.tiledimx, selected_grid[0][0][2]); + } + else if (i == endtilex) { // top right corner + ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[column - 1][0][2]); + } else { // top middle + ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, 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.tiledimx, selected_grid[0][row][2]); + } + else if (i == endtilex) { // bottom right corner + ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[column - 1][row][2]); + } else { // bottom middle + ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[1][row][2]); + } + } else { // middle row + if (i === starttilex) { // middle left + ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[0][(row > 0)? 1 : 0][2]); + } + else if (i === endtilex) { // middle end + ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[column - 1][(row > 0)? 1 : 0][2]); + } else { // middle middle + ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, 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: 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(); \ No newline at end of file diff --git a/patches/src/editor/leconfig.js b/patches/src/editor/leconfig.js index 80629ce24e900da445fef9a086bbc5c68bd3e6b9..47e644bce2b38d0821303e22b18c067bbf5ad602 100644 --- a/patches/src/editor/leconfig.js +++ b/patches/src/editor/leconfig.js @@ -1,41 +1,41 @@ -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; +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; diff --git a/patches/src/editor/lecontext.js b/patches/src/editor/lecontext.js index 3574985effc4a9a2668e5039b00afedd590514b7..11d06d245acda7388391cd92e4883bde36fccb96 100644 --- a/patches/src/editor/lecontext.js +++ b/patches/src/editor/lecontext.js @@ -1,40 +1,40 @@ -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 +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(); \ No newline at end of file diff --git a/patches/src/editor/lehtmlui.js b/patches/src/editor/lehtmlui.js index 8cda068f55c4f969055320f747670a1fed53e3a6..3be5a632214a6b3893829767da1636d08b3688a0 100644 --- a/patches/src/editor/lehtmlui.js +++ b/patches/src/editor/lehtmlui.js @@ -1,134 +1,134 @@ -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]); - } +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]); + } } \ No newline at end of file diff --git a/patches/src/editor/mapfile.js b/patches/src/editor/mapfile.js index f86d936c45f3d3978d1d1c354d478920e01c8f98..f856aafd9b5f27235cb8959a18d246289c592a26 100644 --- a/patches/src/editor/mapfile.js +++ b/patches/src/editor/mapfile.js @@ -1,246 +1,246 @@ -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); +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); } \ No newline at end of file diff --git a/patches/src/editor/maps/gentle-full.js b/patches/src/editor/maps/gentle-full.js index 2e30f23583b5939224edf70f7c135e63d1f55848..494d1a6d534a4c41a27f6411b11d35f6b68474d8 100644 --- a/patches/src/editor/maps/gentle-full.js +++ b/patches/src/editor/maps/gentle-full.js @@ -1,327 +1,327 @@ -// 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" }, -]; +// 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" }, +]; diff --git a/patches/src/editor/maps/gentle.js b/patches/src/editor/maps/gentle.js index 6dda5ecab3e63b0cc5b8f9e392d41539e5cc5b4c..5d987b4aa99778c9734b9e23cc1684488eb8c82a 100644 --- a/patches/src/editor/maps/gentle.js +++ b/patches/src/editor/maps/gentle.js @@ -1,327 +1,327 @@ -// 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" }, -]; +// 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" }, +]; diff --git a/patches/src/editor/maps/gentleanim.js b/patches/src/editor/maps/gentleanim.js index b7f26f8541897d025e4c58f717abe310975a17f0..739d5224b3d0e65b59c2128d2aa169c54de2f103 100644 --- a/patches/src/editor/maps/gentleanim.js +++ b/patches/src/editor/maps/gentleanim.js @@ -1,308 +1,308 @@ -// 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" }, -]; +// 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" }, +]; diff --git a/patches/src/editor/maps/mage3.js b/patches/src/editor/maps/mage3.js index 2e6ce9b9af67b0689f51ab8facf409dfa40c4ac4..deb6dee592ca78d84eaa0a399e09a1e1d58ce4f9 100644 --- a/patches/src/editor/maps/mage3.js +++ b/patches/src/editor/maps/mage3.js @@ -1,220 +1,220 @@ -// 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], -],]; +// 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], +],]; diff --git a/patches/src/editor/maps/serene.js b/patches/src/editor/maps/serene.js index 8a4b1214cca71194b95f01c1233557c93d65a93e..f119f17a996afb25519ac5831f08aedbcbb51da0 100644 --- a/patches/src/editor/maps/serene.js +++ b/patches/src/editor/maps/serene.js @@ -1,276 +1,276 @@ -// 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 , ], -],]; +// 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 , ], +],]; diff --git a/patches/src/editor/se.html b/patches/src/editor/se.html index 1de3a60e14d96a5973c584ea9cee629360b112da..dca031986e59ad7157cac390e70fbe6be55292c0 100644 --- a/patches/src/editor/se.html +++ b/patches/src/editor/se.html @@ -1,92 +1,92 @@ - - - - - - - - -
- - - -
- -
- - - - - - - - - - - - - - -
-
-
-
-
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
- -
- -
- - - - - - - - - - - - - - - -
levelComposite
- - - - - - -
- 16 - 32 -
-
Load Tileset
-
- - - + + + + + + + + +
+ + + +
+ +
+ + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+ + + + + + + + + + + + + + + +
levelComposite
+ + + + + + +
+ 16 + 32 +
+
Load Tileset
+
+ + + \ No newline at end of file diff --git a/patches/src/editor/se.js b/patches/src/editor/se.js index 17a016214a5be8da112661b75553e2d55ff1fced..cd91537e885c67945c796543b5e58a63e1573125 100644 --- a/patches/src/editor/se.js +++ b/patches/src/editor/se.js @@ -1,1217 +1,1217 @@ -// -- -// 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(); -} - +// -- +// 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(); \ No newline at end of file diff --git a/patches/src/editor/seconfig.js b/patches/src/editor/seconfig.js index 87f130481e422f341a785ceef3628b550d92b912..bf54824a6aec6be76e17cac3bf286c4eb6c69115 100644 --- a/patches/src/editor/seconfig.js +++ b/patches/src/editor/seconfig.js @@ -1,54 +1,54 @@ -//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; +//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; diff --git a/patches/src/editor/secontext.js b/patches/src/editor/secontext.js index e682ab25161f26a3f8a0dda35f8f06a0ea8b8e4e..8356fe5b75df98ec0870c7e6d23d1fe0bc8a08f0 100644 --- a/patches/src/editor/secontext.js +++ b/patches/src/editor/secontext.js @@ -1,39 +1,39 @@ -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 +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(); \ No newline at end of file diff --git a/patches/src/editor/sehtmlui.js b/patches/src/editor/sehtmlui.js index 2cc0fea8db30267dc4cc85e9d4a9d7b0493b3ef4..96330a72ff705759f005acb3ca058450a43ec2f2 100644 --- a/patches/src/editor/sehtmlui.js +++ b/patches/src/editor/sehtmlui.js @@ -1,66 +1,66 @@ -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(); - } -} +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(); + } +} diff --git a/patches/src/editor/spritefile.js b/patches/src/editor/spritefile.js index 982eaaf812d6c860fa5004d74825257b31de4517..a1785dbcc05a6b54d3c33ae02c0dbe270b0c82bd 100644 --- a/patches/src/editor/spritefile.js +++ b/patches/src/editor/spritefile.js @@ -1,112 +1,112 @@ -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"); +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"); } \ No newline at end of file diff --git a/patches/src/editor/tilesets/Serene.png b/patches/src/editor/tilesets/Serene.png new file mode 100644 index 0000000000000000000000000000000000000000..8b3ce7a85b470c021090c85be548d98249d144ac Binary files /dev/null and b/patches/src/editor/tilesets/Serene.png differ diff --git a/patches/src/editor/tilesets/forest.png b/patches/src/editor/tilesets/forest.png new file mode 100644 index 0000000000000000000000000000000000000000..2eb8b93c5630f046125bed912058025ed468b8c8 Binary files /dev/null and b/patches/src/editor/tilesets/forest.png differ diff --git a/patches/src/editor/tilesets/gentle.png b/patches/src/editor/tilesets/gentle.png new file mode 100644 index 0000000000000000000000000000000000000000..022c84e183e9ff73763a76979e84e00a0a101d1f Binary files /dev/null and b/patches/src/editor/tilesets/gentle.png differ diff --git a/patches/src/editor/tilesets/magecity.png b/patches/src/editor/tilesets/magecity.png new file mode 100644 index 0000000000000000000000000000000000000000..01010326d296b604e91d30712bad46b48bef84e7 Binary files /dev/null and b/patches/src/editor/tilesets/magecity.png differ diff --git a/patches/src/editor/tilesets/phantasy2.png b/patches/src/editor/tilesets/phantasy2.png new file mode 100644 index 0000000000000000000000000000000000000000..300dbbe7f4f0c38676241dd82dd82fc5962598f9 Binary files /dev/null and b/patches/src/editor/tilesets/phantasy2.png differ diff --git a/patches/src/editor/undo.js b/patches/src/editor/undo.js index 770dfd18140d7953bf9049dc694bf3215f476d45..b5cb5a7752f39b0ddf9ff292b176a9c367effefb 100644 --- a/patches/src/editor/undo.js +++ b/patches/src/editor/undo.js @@ -1,32 +1,32 @@ - -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) { - undoqueu.push(tileindex); -} - -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) { - undo_mark_task_start(layer); - undo_add_index_to_task(tileindex); - undo_mark_task_end(); -} - -export function undo_pop() { - return undo_stack.pop(); + +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) { + undoqueu.push(tileindex); +} + +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) { + undo_mark_task_start(layer); + undo_add_index_to_task(tileindex); + undo_mark_task_end(); +} + +export function undo_pop() { + return undo_stack.pop(); } \ No newline at end of file diff --git a/patches/src/editor/windmill.json b/patches/src/editor/windmill.json index 34ce53cff8a3cd2e275e07a575ca6cf3a1a1e948..8ed04702d619691f53aa4244d27f7f06744787d2 100644 --- a/patches/src/editor/windmill.json +++ b/patches/src/editor/windmill.json @@ -1,77 +1,77 @@ -{"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" -} -} +{"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" +} +} diff --git a/patches/src/hooks/sendInput.ts b/patches/src/hooks/sendInput.ts index bb4e9a678f5f77fb9b05b5c7738635d303d14866..66072616d6a12825736c6ddcc4cad001415d4e98 100644 --- a/patches/src/hooks/sendInput.ts +++ b/patches/src/hooks/sendInput.ts @@ -1,51 +1,51 @@ -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); - }; -} +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); + }; +} diff --git a/patches/src/hooks/serverGame.ts b/patches/src/hooks/serverGame.ts index 3b0c2f217131acfca6199e21e9105bf9184a6b62..6c4c3430b97e7aa072f6e2ce1c317da939bf1c00 100644 --- a/patches/src/hooks/serverGame.ts +++ b/patches/src/hooks/serverGame.ts @@ -1,44 +1,44 @@ -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; -} +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; +} diff --git a/patches/src/hooks/useHistoricalTime.ts b/patches/src/hooks/useHistoricalTime.ts index 83aeba7816c5473a8a80678a7d20769195c51f4f..50bb150860bcea41c7f11e7dcdfdf4602bdf6223 100644 --- a/patches/src/hooks/useHistoricalTime.ts +++ b/patches/src/hooks/useHistoricalTime.ts @@ -1,143 +1,143 @@ -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; +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; diff --git a/patches/src/hooks/useHistoricalValue.ts b/patches/src/hooks/useHistoricalValue.ts index 8f896ce7fcc32bfea3bf89ab6ff0893a4a991c77..f6b202263d7026d7f23d3ea95cbb40313104cfc0 100644 --- a/patches/src/hooks/useHistoricalValue.ts +++ b/patches/src/hooks/useHistoricalValue.ts @@ -1,78 +1,78 @@ -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; - } -} +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; + } +} diff --git a/patches/src/hooks/useWorldHeartbeat.ts b/patches/src/hooks/useWorldHeartbeat.ts index cabbd8dac63a4bc152d140d2a5fa4c5230c2a30b..f9f4ad8431d8914838a6882b3a386dbfc23b4ac5 100644 --- a/patches/src/hooks/useWorldHeartbeat.ts +++ b/patches/src/hooks/useWorldHeartbeat.ts @@ -1,30 +1,30 @@ -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]); -} +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]); +} diff --git a/patches/src/index.css b/patches/src/index.css index c32de4b047723719ecc0a0137e5a338ae5f386cb..dbed329431277a83dab295383b34751dd4d580f9 100644 --- a/patches/src/index.css +++ b/patches/src/index.css @@ -1,185 +1,185 @@ -@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%); -} +@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%); +} diff --git a/patches/src/main.tsx b/patches/src/main.tsx index 0c1b20610f0fdf9e763dc7d7b99ade867ccbc515..00015077ac1a0ffd90ef63a14f7ac19499224f20 100644 --- a/patches/src/main.tsx +++ b/patches/src/main.tsx @@ -1,15 +1,15 @@ -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( - - - - - , -); +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( + + + + + , +); diff --git a/patches/src/toasts.ts b/patches/src/toasts.ts index df7b056220060e4d02bab0b8d7a1fad83acc0485..9862d39455c660d6ae42c8ef3ff36779cc9e323b 100644 --- a/patches/src/toasts.ts +++ b/patches/src/toasts.ts @@ -1,10 +1,10 @@ -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; - } -} +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; + } +} diff --git a/patches/src/vite-env.d.ts b/patches/src/vite-env.d.ts index 7d0ff9efa9d6b0fd823a2f371308a9f92c7771c1..11f02fe2a0061d6e6e1f271b21da95423b448b32 100644 --- a/patches/src/vite-env.d.ts +++ b/patches/src/vite-env.d.ts @@ -1 +1 @@ -/// +///