Started drawing a test canvas, added a few classes and interfaces

This commit is contained in:
Martin Berg Alstad 2023-05-17 20:07:28 +02:00
parent d6cf7ac94d
commit 4d7b0e2b1e
23 changed files with 269 additions and 103 deletions

View File

@ -1,7 +1,6 @@
import React from "react";
import {Counter} from "./pages/Counter";
import {FetchData} from "./pages/FetchData";
import {Home} from "./pages/Home";
import Home from "./pages/home";
const AppRoutes = [
{
@ -12,10 +11,6 @@ const AppRoutes = [
path: "/counter",
element: <Counter/>
},
{
path: "/fetch-data",
element: <FetchData/>
}
];
export default AppRoutes;

View File

@ -1,6 +1,3 @@
type VoidFunction = () => void;
type MessageEventFunction = (data: MessageEvent<any>) => void;
interface IWebSocket {
onOpen?: VoidFunction,
onReceive?: MessageEventFunction,

View File

@ -18,7 +18,6 @@ export const NavMenu = () => {
<ul className="navbar-nav flex-grow">
<Link className="text-dark" to="/">Home</Link>
<Link className="text-dark" to="/counter">Counter</Link>
<Link className="text-dark" to="/fetch-data">Fetch data</Link>
</ul>
</div>
</nav>

View File

@ -0,0 +1,22 @@
import React from "react";
import TileMap from "../game/tileMap";
const tileMap = new TileMap();
const GameCanvas: Component = ({className}) => {
const canvasRef = React.useRef<HTMLCanvasElement>(null);
React.useEffect(() => {
const context = canvasRef.current?.getContext("2d");
if (!context) return;
tileMap.draw(context);
}, []);
return (
<canvas ref={canvasRef} className={`shadow-lg w-3/4 aspect-square ${className}`}></canvas>
);
};
export default GameCanvas;

View File

@ -0,0 +1,19 @@
import React from "react";
import GameCanvas from "../components/gameCanvas";
import Game from "../game/game";
export const GameComponent: Component = () => {
React.useEffect(() => {
const game = new Game();
const id = setInterval(game.gameLoop, 1000);
return () => clearInterval(id);
}, []);
return (
<div>
<h1 className={"w-fit mx-auto"}>Pac-Man</h1>
<GameCanvas className={"mx-auto"}/>
</div>
);
};

View File

@ -0,0 +1,65 @@
export default class Game {
constructor() {
// Connect to the server
// Create players
// Pick player pieces
// Roll to start
}
public gameLoop(): void {
// Throw the dices
// Choose a dice and move pac-man or a ghost
// Use the remaining dice to move pac-man if the player moved a ghost or vice versa
// Check if the game is over
// If not, next player
}
private connectToServer(): void {
throw new Error("Not implemented");
}
private createPlayers(): void {
throw new Error("Not implemented");
}
private pickPlayerPieces(): void {
throw new Error("Not implemented");
}
private rollToStart(): void {
throw new Error("Not implemented");
}
private throwDices(): number[] {
throw new Error("Not implemented");
}
private chooseDice(dices: number[]): number {
throw new Error("Not implemented");
}
private movePacMan(dice: number): void {
throw new Error("Not implemented");
}
private moveGhost(dice: number): void {
throw new Error("Not implemented");
}
private isGameOver(): boolean {
throw new Error("Not implemented");
}
private nextPlayer(): void {
throw new Error("Not implemented");
}
}

View File

@ -0,0 +1,68 @@
export default class TileMap {
/**
* 0 = empty
* 1 = wall
* 2 = pellet
* 3 = power pellet
* 4 = ghost spawn
* 5 = pacman spawn
*/
private map: number[][] = [
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
[1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 5, 1, 0, 1, 4, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1],
[0, 2, 0, 0, 0, 3, 0, 0, 0, 2, 0],
[1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 4, 1, 0, 1, 5, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1],
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
];
public draw(ctx: CanvasRenderingContext2D): void {
this.drawMap(ctx);
}
private drawMap(context: CanvasRenderingContext2D): void {
const tileSize = this.getTileSize(context);
for (let row = 0; row < this.map.length; row++) {
for (let col = 0; col < this.map[row].length; col++) {
const tile = this.map[row][col];
switch (tile) {
case 0:
this.drawTile(context, col * tileSize, row * tileSize, tileSize, "black");
break;
case 1:
this.drawTile(context, col * tileSize, row * tileSize, tileSize, "blue");
break;
case 2:
this.drawTile(context, col * tileSize, row * tileSize, tileSize, "yellow");
break;
case 3:
this.drawTile(context, col * tileSize, row * tileSize, tileSize, "orange");
break;
case 4:
this.drawTile(context, col * tileSize, row * tileSize, tileSize, "red");
break;
}
}
}
}
private drawTile(context: CanvasRenderingContext2D, x: number, y: number, tileSize: number, color: string): void {
context.fillStyle = color;
context.fillRect(x, y, tileSize, tileSize);
}
private getTileSize(context: CanvasRenderingContext2D): number {
const canvasSize = context.canvas.width;
context.canvas.height = canvasSize;
return canvasSize / this.map[0].length;
}
}

View File

@ -5,7 +5,6 @@ const ws = new WebSocketService({});
export const Counter: Component = () => {
ws.onReceive = receiveMessage;
const [currentCount, setCurrentCount] = React.useState(0);
function incrementCounterAndSend() {
@ -21,6 +20,7 @@ export const Counter: Component = () => {
}
React.useEffect(() => {
ws.onReceive = receiveMessage;
ws.open();
return () => {
ws.close();

View File

@ -1,45 +0,0 @@
import React from "react";
export const FetchData: Component = () => {
const [forecasts, setForecasts] = React.useState<any>([]);
const [loading, setLoading] = React.useState(true);
async function populateWeatherData() {
const response = await fetch("api/WeatherForecast");
const data = await response.json();
setForecasts(data);
setLoading(false);
}
React.useEffect(() => {
populateWeatherData().then(null);
}, []);
return <>
{
loading ?
<p><em>Loading...</em></p> :
<table className="table table-striped" aria-labelledby="tableLabel">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
{forecasts.map((forecast: any) =>
<tr key={forecast.date}>
<td>{forecast.date}</td>
<td>{forecast.temperatureC}</td>
<td>{forecast.temperatureF}</td>
<td>{forecast.summary}</td>
</tr>
)}
</tbody>
</table>
}</>;
};

View File

@ -1,32 +0,0 @@
import React from "react";
export const Home: Component = () => (
<div>
<h1 className={"debug w-fit"}>Hello, world!</h1>
<p className={"text-cyan-900"}>Welcome to your new single-page application, built with:</p>
<ul>
<li><a href="https://get.asp.net/">ASP.NET Core</a> and <a
href="https://msdn.microsoft.com/en-us/library/67ef8sbd.aspx">C#</a> for cross-platform server-side
code
</li>
<li><a href="https://facebook.github.io/react/">React</a> for client-side code</li>
</ul>
<p>To help you get started, we have also set up:</p>
<ul>
<li><strong>Client-side navigation</strong>. For example, click <em>Counter</em> then <em>Back</em> to
return here.
</li>
<li><strong>Development server integration</strong>. In development mode, the development server
from <code>create-react-app</code> runs in the background automatically, so your client-side
resources are dynamically built on demand and the page refreshes when you modify any file.
</li>
<li><strong>Efficient production builds</strong>. In production mode, development-time features are
disabled, and your <code>dotnet publish</code> configuration produces minified, efficiently bundled
JavaScript files.
</li>
</ul>
<p>The <code>ClientApp</code> subdirectory is a standard React application based on
the <code>create-react-app</code> template. If you open a command prompt in that directory, you can
run <code>npm</code> commands such as <code>npm test</code> or <code>npm install</code>.</p>
</div>
);

View File

@ -0,0 +1,10 @@
import React from "react";
import {GameComponent} from "../components/gameComponent";
const Home: Component = () => (
<div>
<GameComponent/>
</div>
);
export default Home;

View File

@ -1,5 +1,3 @@
type Setter<T> = React.Dispatch<React.SetStateAction<T>>;
type Component<T = ComponentProps> = (props: T) => React.JSX.Element;
interface ComponentProps {

View File

@ -0,0 +1,3 @@
type MessageEventFunction = (data: MessageEvent<any>) => void;
type Setter<T> = React.Dispatch<React.SetStateAction<T>>;

View File

@ -0,0 +1,6 @@
namespace pacMan.Game.Interfaces;
public interface IBox : IEnumerable<IOrb>
{
void Add(IOrb orb);
}

View File

@ -0,0 +1,6 @@
namespace pacMan.Game.Interfaces;
public interface IDice
{
int Roll();
}

View File

@ -0,0 +1,6 @@
namespace pacMan.Game.Interfaces;
public interface IDiceCup
{
List<int> Roll();
}

View File

@ -0,0 +1,6 @@
namespace pacMan.Game.Interfaces;
public interface IOrb
{
void Use();
}

View File

@ -0,0 +1,10 @@
using pacMan.Game.Interfaces;
namespace pacMan.Game.Items;
public class Dice : IDice
{
private readonly Random _random = new();
public int Roll() => _random.Next(1, 7);
}

View File

@ -0,0 +1,19 @@
using pacMan.Game.Interfaces;
namespace pacMan.Game.Items;
public class DiceCup : IDiceCup
{
private readonly List<Dice> _dices;
public DiceCup()
{
_dices = new List<Dice>
{
new(),
new()
};
}
public List<int> Roll() => _dices.Select(d => d.Roll()).ToList();
}

View File

@ -0,0 +1,9 @@
namespace pacMan.Game;
public class Rules
{
public const int MinPlayers = 2;
public const int MaxPlayers = 4;
public const int NumGhosts = 2;
public const int BoardSize = 10;
}

View File

@ -5,11 +5,12 @@ namespace pacMan.Interfaces;
public interface IWebSocketService
{
void Add(WebSocket webSocket);
void Remove(WebSocket webSocket);
bool Remove(WebSocket webSocket);
Task Send(WebSocket webSocket, string message, int length);
Task Send(WebSocket webSocket, byte[] message, int length);
Task SendToAll(string message, int length);
Task SendToAll(byte[] message, int length);
Task<WebSocketReceiveResult> Receive(WebSocket webSocket, byte[] buffer);
Task Close(WebSocket webSocket, WebSocketCloseStatus closeStatus, string closeStatusDescription);
int CountConnected();
}

View File

@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using System.Net.WebSockets;
using System.Text;
using pacMan.Interfaces;
@ -8,7 +9,7 @@ namespace pacMan.Services;
public class WebSocketService : IWebSocketService
{
private readonly ILogger<WebSocketService> _logger;
private readonly List<WebSocket> _webSockets = new();
private readonly BlockingCollection<WebSocket> _webSockets = new();
public WebSocketService(ILogger<WebSocketService> logger)
{
@ -19,13 +20,14 @@ public class WebSocketService : IWebSocketService
public void Add(WebSocket webSocket)
{
_webSockets.Add(webSocket);
_logger.Log(LogLevel.Debug, "WebSocket \"{}\" added to list", webSocket.GetHashCode());
_logger.Log(LogLevel.Debug, "WebSocket added to list");
}
public void Remove(WebSocket webSocket)
public bool Remove(WebSocket? webSocket)
{
_webSockets.Remove(webSocket);
_logger.Log(LogLevel.Debug, "WebSocket \"{}\" removed from list", webSocket.GetHashCode());
var taken = _webSockets.TryTake(out webSocket);
_logger.Log(LogLevel.Debug, "WebSocket removed from list");
return taken;
}
public async Task Send(WebSocket webSocket, string message, int length)
@ -44,9 +46,8 @@ public class WebSocketService : IWebSocketService
CancellationToken.None);
_logger.Log(LogLevel.Trace,
"Message \"{}\" sent to WebSocket {}",
message.GetString(length),
webSocket.GetHashCode());
"Message \"{}\" sent to WebSocket",
message.GetString(length));
}
public async Task SendToAll(string message, int length)
@ -66,9 +67,8 @@ public class WebSocketService : IWebSocketService
{
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
_logger.Log(LogLevel.Debug,
"Message \"{}\" received from WebSocket {}",
buffer.GetString(result.Count),
webSocket.GetHashCode());
"Message \"{}\" received from WebSocket",
buffer.GetString(result.Count));
return result;
}
@ -79,6 +79,8 @@ public class WebSocketService : IWebSocketService
closeStatus,
closeStatusDescription,
CancellationToken.None);
_logger.Log(LogLevel.Information, "WebSocket connection closed from {}", webSocket.GetHashCode());
_logger.Log(LogLevel.Information, "WebSocket connection closed");
}
public int CountConnected() => _webSockets.Count;
}

View File

@ -33,6 +33,8 @@
<TypeScriptCompile Remove="ClientApp\src\components\Counter.tsx" />
<TypeScriptCompile Remove="ClientApp\src\components\FetchData.tsx" />
<TypeScriptCompile Remove="ClientApp\src\components\Home.tsx" />
<TypeScriptCompile Remove="ClientApp\src\pages\FetchData.tsx" />
<TypeScriptCompile Remove="ClientApp\src\classes\tileMap.ts" />
</ItemGroup>
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">