mirror of
https://github.com/Xevion/100prisoners.git
synced 2026-01-31 08:23:25 -06:00
Project init
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
# Since .env is gitignored, you can use .env.example to build a new `.env` file when you clone the repo.
|
||||||
|
# Keep this file up-to-date when you add new variables to `.env`.
|
||||||
|
|
||||||
|
# This file will be committed to version control, so make sure not to have any secrets in it.
|
||||||
|
# If you are cloning this repo, create a copy of this file named `.env` and populate it with your secrets.
|
||||||
|
|
||||||
|
# When adding additional env variables, the schema in /env/schema.mjs should be updated accordingly
|
||||||
|
|
||||||
|
# Example:
|
||||||
|
# SERVERVAR=foo
|
||||||
|
# NEXT_PUBLIC_CLIENTVAR=bar
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"project": "./tsconfig.json"
|
||||||
|
},
|
||||||
|
"plugins": ["@typescript-eslint"],
|
||||||
|
"extends": ["next/core-web-vitals", "plugin:@typescript-eslint/recommended"],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/consistent-type-imports": "warn"
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+42
@@ -0,0 +1,42 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# database
|
||||||
|
/prisma/db.sqlite
|
||||||
|
/prisma/db.sqlite-journal
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
next-env.d.ts
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
|
||||||
|
.env
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Create T3 App
|
||||||
|
|
||||||
|
This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`.
|
||||||
|
|
||||||
|
## What's next? How do I make an app with this?
|
||||||
|
|
||||||
|
We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary.
|
||||||
|
|
||||||
|
If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help.
|
||||||
|
|
||||||
|
- [Next.js](https://nextjs.org)
|
||||||
|
- [NextAuth.js](https://next-auth.js.org)
|
||||||
|
- [Prisma](https://prisma.io)
|
||||||
|
- [Tailwind CSS](https://tailwindcss.com)
|
||||||
|
- [tRPC](https://trpc.io)
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources:
|
||||||
|
|
||||||
|
- [Documentation](https://create.t3.gg/)
|
||||||
|
- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials
|
||||||
|
|
||||||
|
You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## How do I deploy this?
|
||||||
|
|
||||||
|
Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel) and [Docker](https://create.t3.gg/en/deployment/docker) for more information.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// @ts-check
|
||||||
|
/**
|
||||||
|
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
|
||||||
|
* This is especially useful for Docker builds.
|
||||||
|
*/
|
||||||
|
!process.env.SKIP_ENV_VALIDATION && (await import("./src/env/server.mjs"));
|
||||||
|
|
||||||
|
/** @type {import("next").NextConfig} */
|
||||||
|
const config = {
|
||||||
|
reactStrictMode: true,
|
||||||
|
swcMinify: true,
|
||||||
|
i18n: {
|
||||||
|
locales: ["en"],
|
||||||
|
defaultLocale: "en",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"name": "100prisoners",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "next build",
|
||||||
|
"dev": "next dev",
|
||||||
|
"lint": "next lint",
|
||||||
|
"start": "next start"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/react-query": "^4.16.0",
|
||||||
|
"@trpc/client": "^10.0.0",
|
||||||
|
"@trpc/next": "^10.0.0",
|
||||||
|
"@trpc/react-query": "^10.0.0",
|
||||||
|
"@trpc/server": "^10.0.0",
|
||||||
|
"@types/chance": "^1.1.3",
|
||||||
|
"chance": "^1.1.9",
|
||||||
|
"next": "13.0.2",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"react-dom": "18.2.0",
|
||||||
|
"sass": "^1.57.0",
|
||||||
|
"superjson": "1.9.1",
|
||||||
|
"zod": "^3.18.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^18.0.0",
|
||||||
|
"@types/react": "^18.0.14",
|
||||||
|
"@types/react-dom": "^18.0.5",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
||||||
|
"@typescript-eslint/parser": "^5.33.0",
|
||||||
|
"autoprefixer": "^10.4.7",
|
||||||
|
"eslint": "^8.26.0",
|
||||||
|
"eslint-config-next": "13.0.2",
|
||||||
|
"postcss": "^8.4.14",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||||
|
"tailwindcss": "^3.2.0",
|
||||||
|
"typescript": "^4.8.4"
|
||||||
|
},
|
||||||
|
"ct3aMetadata": {
|
||||||
|
"initVersion": "6.11.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/** @type {import("prettier").Config} */
|
||||||
|
module.exports = {
|
||||||
|
plugins: [require.resolve("prettier-plugin-tailwindcss")],
|
||||||
|
};
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
// @ts-check
|
||||||
|
import { clientEnv, clientSchema } from "./schema.mjs";
|
||||||
|
|
||||||
|
const _clientEnv = clientSchema.safeParse(clientEnv);
|
||||||
|
|
||||||
|
export const formatErrors = (
|
||||||
|
/** @type {import('zod').ZodFormattedError<Map<string,string>,string>} */
|
||||||
|
errors,
|
||||||
|
) =>
|
||||||
|
Object.entries(errors)
|
||||||
|
.map(([name, value]) => {
|
||||||
|
if (value && "_errors" in value)
|
||||||
|
return `${name}: ${value._errors.join(", ")}\n`;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
if (!_clientEnv.success) {
|
||||||
|
console.error(
|
||||||
|
"❌ Invalid environment variables:\n",
|
||||||
|
...formatErrors(_clientEnv.error.format()),
|
||||||
|
);
|
||||||
|
throw new Error("Invalid environment variables");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let key of Object.keys(_clientEnv.data)) {
|
||||||
|
if (!key.startsWith("NEXT_PUBLIC_")) {
|
||||||
|
console.warn(
|
||||||
|
`❌ Invalid public environment variable name: ${key}. It must begin with 'NEXT_PUBLIC_'`,
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new Error("Invalid public environment variable name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const env = _clientEnv.data;
|
||||||
Vendored
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// @ts-check
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify your server-side environment variables schema here.
|
||||||
|
* This way you can ensure the app isn't built with invalid env vars.
|
||||||
|
*/
|
||||||
|
export const serverSchema = z.object({
|
||||||
|
NODE_ENV: z.enum(["development", "test", "production"]),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify your client-side environment variables schema here.
|
||||||
|
* This way you can ensure the app isn't built with invalid env vars.
|
||||||
|
* To expose them to the client, prefix them with `NEXT_PUBLIC_`.
|
||||||
|
*/
|
||||||
|
export const clientSchema = z.object({
|
||||||
|
// NEXT_PUBLIC_CLIENTVAR: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You can't destruct `process.env` as a regular object, so you have to do
|
||||||
|
* it manually here. This is because Next.js evaluates this at build time,
|
||||||
|
* and only used environment variables are included in the build.
|
||||||
|
* @type {{ [k in keyof z.infer<typeof clientSchema>]: z.infer<typeof clientSchema>[k] | undefined }}
|
||||||
|
*/
|
||||||
|
export const clientEnv = {
|
||||||
|
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR,
|
||||||
|
};
|
||||||
Vendored
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// @ts-check
|
||||||
|
/**
|
||||||
|
* This file is included in `/next.config.mjs` which ensures the app isn't built with invalid env vars.
|
||||||
|
* It has to be a `.mjs`-file to be imported there.
|
||||||
|
*/
|
||||||
|
import { serverSchema } from "./schema.mjs";
|
||||||
|
import { env as clientEnv, formatErrors } from "./client.mjs";
|
||||||
|
|
||||||
|
const _serverEnv = serverSchema.safeParse(process.env);
|
||||||
|
|
||||||
|
if (!_serverEnv.success) {
|
||||||
|
console.error(
|
||||||
|
"❌ Invalid environment variables:\n",
|
||||||
|
...formatErrors(_serverEnv.error.format()),
|
||||||
|
);
|
||||||
|
throw new Error("Invalid environment variables");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let key of Object.keys(_serverEnv.data)) {
|
||||||
|
if (key.startsWith("NEXT_PUBLIC_")) {
|
||||||
|
console.warn("❌ You are exposing a server-side env-variable:", key);
|
||||||
|
|
||||||
|
throw new Error("You are exposing a server-side env-variable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const env = { ..._serverEnv.data, ...clientEnv };
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { type AppType } from "next/app";
|
||||||
|
|
||||||
|
import { trpc } from "../utils/trpc";
|
||||||
|
|
||||||
|
import "../styles/globals.scss";
|
||||||
|
|
||||||
|
const MyApp: AppType = ({ Component, pageProps }) => {
|
||||||
|
return <Component {...pageProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default trpc.withTRPC(MyApp);
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { createNextApiHandler } from "@trpc/server/adapters/next";
|
||||||
|
|
||||||
|
import { env } from "../../../env/server.mjs";
|
||||||
|
import { createContext } from "../../../server/trpc/context";
|
||||||
|
import { appRouter } from "../../../server/trpc/router/_app";
|
||||||
|
|
||||||
|
// export API handler
|
||||||
|
export default createNextApiHandler({
|
||||||
|
router: appRouter,
|
||||||
|
createContext,
|
||||||
|
onError:
|
||||||
|
env.NODE_ENV === "development"
|
||||||
|
? ({ path, error }) => {
|
||||||
|
console.error(`❌ tRPC failed on ${path}: ${error}`);
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import {type NextPage} from "next";
|
||||||
|
import Head from "next/head";
|
||||||
|
import BoxGraphic from "../components/BoxGraphic";
|
||||||
|
import Chance from "chance";
|
||||||
|
import {range} from "../utils/helpers";
|
||||||
|
|
||||||
|
const chance = new Chance();
|
||||||
|
|
||||||
|
const Home: NextPage = () => {
|
||||||
|
const sources = range(0, 100);
|
||||||
|
const destinations = chance.shuffle(sources);
|
||||||
|
const boxes = sources.map((e, i) => [e, destinations[i]])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>100prisoners.com</title>
|
||||||
|
<link rel="icon" href="/favicon.ico"/>
|
||||||
|
</Head>
|
||||||
|
<main className="flex min-h-screen flex-col items-center bg-white">
|
||||||
|
<div className="mt-8 px-3 w-[50rem] space-y-3">
|
||||||
|
<h1 className="text-4xl mb-2">100Prisoners.com</h1>
|
||||||
|
<div>
|
||||||
|
This website is dedicated to exploring the intriguing 100 prisoners problem, a mathematical
|
||||||
|
challenge that seems astronomically impossible at first, yet can leverage mathematics to raise
|
||||||
|
the chances one hundred octillion.
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
This thought experiment presents a scenario in which a group of 100 prisoners are tasked with
|
||||||
|
finding their own numbered slip among a collection of 100 boxes, each containing a random
|
||||||
|
permutation of the numbers 1 through 100. The prisoners are allowed to open 50 boxes each in an
|
||||||
|
attempt to find their own number, and all of the prisoners must be successful in order to be set
|
||||||
|
free. This problem raises questions about strategy and probability in search of a solution.
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-10 w-full space-y-2">
|
||||||
|
{boxes.map(([source, destination]) =>
|
||||||
|
<div className="col-span-1 px-2">
|
||||||
|
<div key={source} className="box aspect-square relative">
|
||||||
|
<span className="absolute left-6 top-8 cursor-pointer">{destination}</span>
|
||||||
|
<BoxGraphic className="transition-all cursor-pointer relative z-30">
|
||||||
|
{source + 1}
|
||||||
|
</BoxGraphic>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { type inferAsyncReturnType } from "@trpc/server";
|
||||||
|
import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace this with an object if you want to pass things to createContextInner
|
||||||
|
*/
|
||||||
|
type CreateContextOptions = Record<string, never>;
|
||||||
|
|
||||||
|
/** Use this helper for:
|
||||||
|
* - testing, so we dont have to mock Next.js' req/res
|
||||||
|
* - trpc's `createSSGHelpers` where we don't have req/res
|
||||||
|
* @see https://create.t3.gg/en/usage/trpc#-servertrpccontextts
|
||||||
|
**/
|
||||||
|
export const createContextInner = async (opts: CreateContextOptions) => {
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the actual context you'll use in your router
|
||||||
|
* @link https://trpc.io/docs/context
|
||||||
|
**/
|
||||||
|
export const createContext = async (opts: CreateNextContextOptions) => {
|
||||||
|
return await createContextInner({});
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Context = inferAsyncReturnType<typeof createContext>;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { router } from "../trpc";
|
||||||
|
import { exampleRouter } from "./example";
|
||||||
|
|
||||||
|
export const appRouter = router({
|
||||||
|
example: exampleRouter,
|
||||||
|
});
|
||||||
|
|
||||||
|
// export type definition of API
|
||||||
|
export type AppRouter = typeof appRouter;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { router, publicProcedure } from "../trpc";
|
||||||
|
|
||||||
|
export const exampleRouter = router({
|
||||||
|
hello: publicProcedure
|
||||||
|
.input(z.object({ text: z.string().nullish() }).nullish())
|
||||||
|
.query(({ input }) => {
|
||||||
|
return {
|
||||||
|
greeting: `Hello ${input?.text ?? "world"}`,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { initTRPC } from "@trpc/server";
|
||||||
|
import superjson from "superjson";
|
||||||
|
|
||||||
|
import { type Context } from "./context";
|
||||||
|
|
||||||
|
const t = initTRPC.context<Context>().create({
|
||||||
|
transformer: superjson,
|
||||||
|
errorFormatter({ shape }) {
|
||||||
|
return shape;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const router = t.router;
|
||||||
|
|
||||||
|
export const publicProcedure = t.procedure;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const range = (start: number, stop: number, step = 1) =>
|
||||||
|
Array.from({length: (stop - start) / step + 1}, (_, i) => start + i * step);
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { httpBatchLink, loggerLink } from "@trpc/client";
|
||||||
|
import { createTRPCNext } from "@trpc/next";
|
||||||
|
import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server";
|
||||||
|
import superjson from "superjson";
|
||||||
|
|
||||||
|
import { type AppRouter } from "../server/trpc/router/_app";
|
||||||
|
|
||||||
|
const getBaseUrl = () => {
|
||||||
|
if (typeof window !== "undefined") return ""; // browser should use relative url
|
||||||
|
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url
|
||||||
|
return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost
|
||||||
|
};
|
||||||
|
|
||||||
|
export const trpc = createTRPCNext<AppRouter>({
|
||||||
|
config() {
|
||||||
|
return {
|
||||||
|
transformer: superjson,
|
||||||
|
links: [
|
||||||
|
loggerLink({
|
||||||
|
enabled: (opts) =>
|
||||||
|
process.env.NODE_ENV === "development" ||
|
||||||
|
(opts.direction === "down" && opts.result instanceof Error),
|
||||||
|
}),
|
||||||
|
httpBatchLink({
|
||||||
|
url: `${getBaseUrl()}/api/trpc`,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inference helper for inputs
|
||||||
|
* @example type HelloInput = RouterInputs['example']['hello']
|
||||||
|
**/
|
||||||
|
export type RouterInputs = inferRouterInputs<AppRouter>;
|
||||||
|
/**
|
||||||
|
* Inference helper for outputs
|
||||||
|
* @example type HelloOutput = RouterOutputs['example']['hello']
|
||||||
|
**/
|
||||||
|
export type RouterOutputs = inferRouterOutputs<AppRouter>;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ["./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
playfair: ["\"Playfair Display\"", "serif"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2017",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"noUncheckedIndexedAccess": true
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.mjs"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user