Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ yarn-error.log*
sui.log.*
.history
*.Identifier
.next

12 changes: 12 additions & 0 deletions move202503/PebblerWon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## project

- 项目名称: Fruit Hunter
> 描述: 一个小游戏,用户通过点击棋盘上的水果来进行游戏,点击正确获得代币,点击失败失去代币

![](./public/loss.png)
![](./public/win.png)

## Member

- PebblerWOn github: [PebblerWon](https://github.com/PebblerWon)
> 自我介绍&技术栈: 前端
71 changes: 71 additions & 0 deletions move202503/PebblerWon/app/components/board/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { CellStatus, Diffculty } from "@/app/components/game/definitions";
import { useGame } from "@/app/components/game/useGame";
import React from "react";
import { Game } from "../game/Game";
const positionArr = [
[7, 5],
[-34, 5],
[-78, 4],
[-123, 2],
[-167, 2],
];
function Board(props: {
diffculty: Diffculty;
game: Game;
verify: (index: number, diffculty: Diffculty) => void;
}) {
const { game, verify } = props;

return (
<div className={`h-full grid grid-rows-6 grid-cols-4 gap-4`}>
{game.board.flat().map((row, i) => {
let imageStyle;
if (row == CellStatus.Bad) {
imageStyle = {
backgroundImage: `url(skull.webp)`,
backgroundSize: "100% 100%",
backgroundColor: "transparent",
borderRadius: 0,
};
} else {
imageStyle = {
backgroundImage: `url(fruit_bg.webp)`,
backgroundSize: "200px 30px",
backgroundPosition: `${positionArr[i % 5][0]}px ${
positionArr[i % 5][1]
}px`,
};
if (row == CellStatus.Good) {
imageStyle.backgroundImage = `url(fruit_green.webp)`;
}
}

return (
<div
key={i}
className="cursor-pointer rounded-md flex items-center justify-center bg-[#3a4142] hover:bg-[#4a5354]"
style={{
backgroundImage: `url(cell_bg.png)`,
}}
onClick={() => {
verify(i, props.diffculty);
}}
>
<div
style={{
backgroundColor: "rgb(33,35,40)",
backgroundRepeat: "no-repeat",
width: 40,
height: 40,
borderRadius: "100%",
...imageStyle,
}}
></div>
</div>
);
})}
</div>
);
}

export default Board;
3 changes: 3 additions & 0 deletions move202503/PebblerWon/app/components/fonts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Inter, Lusitana } from "next/font/google";
export const inter = Inter({ subsets: ["latin"] });
export const lusitana = Lusitana({ weight: "400", subsets: ["latin"] });
87 changes: 87 additions & 0 deletions move202503/PebblerWon/app/components/game/Game.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
CellStatus,
Diffculty,
GameStatus,
getRandomElements,
} from "./definitions";

type Board = number[];
export enum GameVerifyStatus {
Win = 1,
Loss = 0,
Error = -1,
}
export class Game {
diffculty?: Diffculty;
status: GameStatus;
board: Board;
creator?: string;
gameId?: string;
boardWidth = 4;
boardHeight = 6;
gameResult?: GameVerifyStatus;
constructor() {
this.status = GameStatus.Ready;
this.board = [];
this.initialGame();
}

initialGame() {
for (let i = 0; i < this.boardHeight * this.boardWidth; i++) {
this.board[i] = CellStatus.ToVerify;
}
this.gameResult = undefined;
}
verify(index: number, diffculty: Diffculty, result: boolean) {
const clickedCell = this.board[index];
if (clickedCell == undefined) GameVerifyStatus.Error;
if (clickedCell !== CellStatus.ToVerify) {
return GameVerifyStatus.Error;
}
if (result) {
this.board[index] = CellStatus.Good;
this.gameOver(true, index, diffculty);
return GameVerifyStatus.Win;
} else {
// game over
this.board[index] = CellStatus.Bad;
this.gameOver(false, index, diffculty);
return GameVerifyStatus.Loss;
}
}

gameOver(result: boolean, index: number, diffculty: Diffculty) {
this.status = GameStatus.Done;

let badRate = 0;
if (diffculty == Diffculty.EASY) {
badRate = 12;
} else if (diffculty == Diffculty.MIDDLE) {
badRate = 16;
} else if (diffculty == Diffculty.HARD) {
badRate = 18;
}
const a = getRandomElements(this.boardWidth * this.boardHeight, badRate);
for (let i = 0; i < a.length; i++) {
if (this.board[a[i]] == CellStatus.ToVerify) {
this.board[a[i]] = CellStatus.Bad;
}
}
if (result) {
this.board[index] = CellStatus.Good;
this.gameResult = GameVerifyStatus.Win;
} else {
this.board[index] = CellStatus.Bad;
this.gameResult = GameVerifyStatus.Loss;
}
this.status = GameStatus.Done;
}
running() {
this.status = GameStatus.Running;
}
reset() {
this.status = GameStatus.Ready;
this.board = [];
this.initialGame();
}
}
115 changes: 115 additions & 0 deletions move202503/PebblerWon/app/components/game/definitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// This file contains type definitions for your data.
// It describes the shape of the data, and what data type each property should accept.
// For simplicity of teaching, we're manually defining these types.
// However, these types are generated automatically if you're using an ORM such as Prisma.
export type User = {
id: string;
name: string;
email: string;
password: string;
};

export type Customer = {
id: string;
name: string;
email: string;
image_url: string;
};

export type Invoice = {
id: string;
customer_id: string;
amount: number;
date: string;
// In TypeScript, this is called a string union type.
// It means that the "status" property can only be one of the two strings: 'pending' or 'paid'.
status: "pending" | "paid";
};

export type Revenue = {
month: string;
revenue: number;
};

export type LatestInvoice = {
id: string;
name: string;
image_url: string;
email: string;
amount: string;
};

// The database returns a number for amount, but we later format it to a string with the formatCurrency function
export type LatestInvoiceRaw = Omit<LatestInvoice, "amount"> & {
amount: number;
};

export type InvoicesTable = {
id: string;
customer_id: string;
name: string;
email: string;
image_url: string;
date: string;
amount: number;
status: "pending" | "paid";
};

export type CustomersTableType = {
id: string;
name: string;
email: string;
image_url: string;
total_invoices: number;
total_pending: number;
total_paid: number;
};

export type FormattedCustomersTable = {
id: string;
name: string;
email: string;
image_url: string;
total_invoices: number;
total_pending: string;
total_paid: string;
};

export type CustomerField = {
id: string;
name: string;
};

export type InvoiceForm = {
id: string;
customer_id: string;
amount: number;
status: "pending" | "paid";
};

export enum Diffculty {
EASY = 1,
MIDDLE = 2,
HARD = 3,
}

export enum GameStatus {
Ready = 0,
Running = 1,
Done = 2,
}

export enum CellStatus {
ToVerify = 0,
Good = 1,
Bad = 2,
}

export function getRandomElements(length: number, count: number) {
const shuffled = Array.from({ length }, (_, i) => i);
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled.slice(0, count);
}
Loading