diff --git a/ipc/pythonHandlers.js b/ipc/pythonHandlers.js index 6712c37..c8ca301 100644 --- a/ipc/pythonHandlers.js +++ b/ipc/pythonHandlers.js @@ -45,7 +45,6 @@ function closePython(){ } } - class TcpClientManager { constructor() { this.client = null; @@ -54,12 +53,36 @@ class TcpClientManager { this.messageBuffer = ''; // For handling partial messages } - connect(host, port, onData, onMessage, onComplete, onError, onClose) { + async connect(host, port, onData, onMessage, onComplete, onError, onClose, maxRetries = 20, initialDelay = 100) { + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + await this._attemptConnect(host, port, onData, onMessage, onComplete, onError, onClose); + console.log(`Successfully connected to TCP server on port ${port}`); + return; // Success! + } catch (error) { + if (attempt === maxRetries - 1) { + throw new Error(`Failed to connect after ${maxRetries} attempts: ${error.message}`); + } + + const delay = Math.min(initialDelay * Math.pow(1.5, attempt), 2000); + console.log(`Connection attempt ${attempt + 1} failed, retrying in ${delay}ms...`); + await new Promise(resolve => setTimeout(resolve, delay)); + } + } + } + + _attemptConnect(host, port, onData, onMessage, onComplete, onError, onClose) { return new Promise((resolve, reject) => { - // crate tcp client + // Create tcp client this.client = new net.Socket(); - //start registering handlers + // Set connection timeout + const connectionTimeout = setTimeout(() => { + this.client.destroy(); + reject(new Error('Connection timeout')); + }, 2000); + + // Start registering handlers this.client.on('data', (data) => { const chunk = data.toString(); this.dataBuffer += chunk; @@ -79,7 +102,7 @@ class TcpClientManager { onMessage(jsonMessage); } catch (e) { console.warn('Received non-JSON message:', message); - onMessage({ type:"unparsed" , raw:message }); + onMessage({ type: "unparsed", raw: message }); } } } @@ -90,45 +113,46 @@ class TcpClientManager { this.connected = false; if (onComplete) { - onComplete() - }; + onComplete(); + } }); this.client.on('close', () => { console.log('TCP connection closed'); this.connected = false; if (onClose) { - onClose() - }; + onClose(); + } }); this.client.on('error', (err) => { + clearTimeout(connectionTimeout); console.error('TCP client error:', err); - if (onError) { + + // Only call onError for errors after successful connection + if (this.connected && onError) { onError(err); } + this.connected = false; - reject(err); }); // Actually connect this.client.connect(port, host, () => { + clearTimeout(connectionTimeout); console.log(`TCP client connected to ${host}:${port}`); this.connected = true; resolve(); }); - - }); } - disconnect(){ + disconnect() { if (this.client && this.connected) { - this.client.end() + this.client.end(); } - this.connected = false - + this.connected = false; } sendInterrupt() { @@ -141,6 +165,8 @@ class TcpClientManager { } } + + function setupPythonScriptHandlers(store, enginePath) { ipcMain.handle('run-python-script', async (event, scriptArgs) => { @@ -163,7 +189,11 @@ function setupPythonScriptHandlers(store, enginePath) { // Create new TCP client manager tcpClientManager = new TcpClientManager(); - pythonProcess = spawn(`"${pythonpath} ${gameScript}"`, [...scriptArgs], { + + console.log("running") + console.log(`"${pythonpath} ${gameScript} ${[...scriptArgs]}"`) + + pythonProcess = spawn(`"${pythonpath}" "${gameScript}"`, [...scriptArgs], { cwd: enginePath, shell: true }); @@ -197,7 +227,7 @@ function setupPythonScriptHandlers(store, enginePath) { if (code !== 0) { reject(new Error(`Python script error: ${scriptError}`)); } else { - resolve(tcpResult); + resolve(scriptOutput); } }); @@ -219,7 +249,7 @@ function setupPythonScriptHandlers(store, enginePath) { console.log('TCP data received'); }, (jsonData) => { // onMessage - event.sender.send('stream-tcp-message', jsonData); + event.sender.send('stream-tcp-json', jsonData); console.log('TCP data parsed'); tcpResult = jsonData; }, diff --git a/package.json b/package.json index 37ec6e9..4c1e0f9 100755 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "name": "bytefight-client", + "name": "bytefight-client-2026", "version": "1.1.0", "private": true, "main": "main.js", "homepage": "./", - "description": "Bytefight Desktop Client", + "description": "Bytefight 2026 Desktop Client", "author": "Henry Liao ", "scripts": { "dev": "next dev", @@ -57,8 +57,8 @@ "typescript": "^5.8.2" }, "build": { - "appId": "com.bytefight.bytefightclient2025", - "productName": "Bytefight Client 2025", + "appId": "com.bytefight.bytefightclient2026", + "productName": "Bytefight Client 2026", "forceCodeSigning": false, "files": [ { @@ -70,7 +70,8 @@ }, "main.js", "preload.js", - "package.json" + "package.json", + "ipc/**" ], "extraResources": [ { diff --git a/preload.js b/preload.js index 8f7ab9c..4397241 100644 --- a/preload.js +++ b/preload.js @@ -26,6 +26,24 @@ contextBridge.exposeInMainWorld('electron', { sendTCPInterrupt: () => ipcRenderer.invoke('tcp-send-interrupt'), disconnectTCP: () => ipcRenderer.invoke('tcp-disconnect'), + onTcpData: (callback) => { + const handler = (_, data) => callback(data); + ipcRenderer.on('stream-tcp-data', handler); + return () => ipcRenderer.removeListener('stream-tcp-data', handler); + }, + + onTcpJson: (callback) => { + const handler = (_, data) => callback(data); + ipcRenderer.on('stream-tcp-message', handler); + return () => ipcRenderer.removeListener('stream-tcp-message', handler); + }, + + onTcpStatus: (callback) => { + const handler = (_, status) => callback(status); + ipcRenderer.on('stream-tcp-status', handler); + return () => ipcRenderer.removeListener('stream-tcp-status', handler); + }, + onStreamOutput: (callback) => { const handler = (_, chunk) => callback(chunk); ipcRenderer.on('stream-output', handler); @@ -49,20 +67,4 @@ contextBridge.exposeInMainWorld('electron', { ipcRenderer.on('stream-error-full', handler); return () => ipcRenderer.removeListener('stream-error-full', handler); }, - - onTcpData: (callback) => { - const handler = (_, data) => callback(data); - ipcRenderer.on('stream-tcp-data', handler); - return () => ipcRenderer.removeListener('stream-tcp-data', handler); - }, - onTcpJson: (callback) => { - const handler = (_, data) => callback(data); - ipcRenderer.on('stream-tcp-message', handler); - return () => ipcRenderer.removeListener('stream-tcp-message', handler); - }, - onTcpStatus: (callback) => { - const handler = (_, status) => callback(status); - ipcRenderer.on('stream-tcp-status', handler); - return () => ipcRenderer.removeListener('stream-tcp-status', handler); - }, }); \ No newline at end of file diff --git a/src/components/CellSelector.js b/src/components/CellSelector.js index 7dcccce..688ca5e 100644 --- a/src/components/CellSelector.js +++ b/src/components/CellSelector.js @@ -1,7 +1,7 @@ import { useState } from 'react' export default function CellSelector({handleCellChange}) { - const [ids, setIds] = useState(["Space", "Wall", "Snake A", "Snake B", "Portal 1", "Portal 2"]); + const [ids, setIds] = useState(["Space", "Wall", "Player 1", "Player 2", "Hill"]); return ( diff --git a/src/components/MapBuilder.js b/src/components/MapBuilder.js index 49b9764..2030be4 100644 --- a/src/components/MapBuilder.js +++ b/src/components/MapBuilder.js @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import MapSettings from './MapSettings'; import ShowSpawn from './ShowSpawn'; -import MapVis from './MapVis'; +import MapBuilderVis from './MapBuilderVis'; import CellSelector from './CellSelector' import SymmetrySelector from './SymmetrySelector' import { Button } from '@/components/ui/button'; @@ -12,43 +12,38 @@ const GridValues = { EMPTY: 0, WALL: 1, APPLE: 2, - SNAKE_A_HEAD: 3, - SNAKE_A_BODY: 4, - SNAKE_B_HEAD: 5, - SNAKE_B_BODY: 6, - START_PORTAL: 7, - END_PORTAL: 8 + PLAYER_1: 3, + PLAYER_2: 5, + HILL: 7, } export default function MapBuilder() { - const [showSnakeStart, setShowSnakeStart] = useState(true); + const [showSpawn, setShowSpawn] = useState(true); const [aSpawn, setASpawn] = useState([-1, -1]); const [bSpawn, setBSpawn] = useState([-1, -1]); const [mapHeight, setMapHeight] = useState(20); const [mapWidth, setMapWidth] = useState(20); const [walls, setWalls] = useState(null); // Array to store wall positions, initially empty - const [portals, setPortals] = useState(null); // Array to store wall positions, initially empty const [cellType, setCellType] = useState(GridValues.EMPTY); - const [appleRate, setAppleRate] = useState(50); - const [appleNum, setAppleNum] = useState(1); + const [powerupRate, setPowerupRate] = useState(50); + const [powerupNum, setPowerupNum] = useState(1); const [symmetry, setSymmetry] = useState("Vertical"); const [canvasRerender, setCanvasRerender] = useState(false) - const [startSize, setStartSize] = useState(5) const [mapName, setMapName] = useState("") - const [startPortal, setStartPortal] = useState([-1, -1]) - const [endPortal, setEndPortal] = useState([-1, -1]) + const [hillGrid, setHillGrid] = useState(null) // Array to store wall positions, initially empty + const [hillID, setHillID] = useState(1) + const { toast } = useToast(); const min_map = 1; const max_map = 64; - const min_apple_num = 1; - const max_apple_num = 1000; - const min_apple_rate = 1; - const max_apple_rate = 200; - const min_start_size = 2; - const max_start_size = 1000; - const min_size = 2; + const min_powerup_num = 1; + const max_powerup_num = 1000; + const min_powerup_rate = 1; + const max_powerup_rate = 200; + const min_hill_id = 1; + const max_hill_id = 1000; const reflect = (x, y) => { if (symmetry == "Vertical") { @@ -72,26 +67,18 @@ export default function MapBuilder() { case "Space": setCellType(GridValues.EMPTY); break; - // case "Apple": - // setCellType(GridValues.APPLE); - // break; case "Wall": setCellType(GridValues.WALL); break; - case "Snake A": - - setCellType(GridValues.SNAKE_A_HEAD); + case "Player 1": + setCellType(GridValues.PLAYER_1); break; - case "Snake B": - - setCellType(GridValues.SNAKE_B_HEAD); + case "Player 2": + setCellType(GridValues.PLAYER_2); break; - case "Portal 1": - setCellType(GridValues.START_PORTAL) + case "Hill": + setCellType(GridValues.HILL) break; - case "Portal 2": - setCellType(GridValues.END_PORTAL) - break } }; @@ -101,11 +88,10 @@ export default function MapBuilder() { const f = Math.max(Math.min(max_map, value), min_map) setMapHeight(f); setWalls(new Array(f).fill().map(() => new Array(mapWidth).fill(false))); - setPortals(new Array(f).fill().map(() => new Array(mapWidth).fill(-1))); + setHillGrid(new Array(f).fill().map(() => new Array(mapWidth).fill(0))); setASpawn([-1, -1]) setBSpawn([-1, -1]) - setStartPortal([-1, -1]) - setEndPortal([-1, -1]) + setHillID(1) }; const handleWidthChange = (event) => { @@ -113,34 +99,32 @@ export default function MapBuilder() { const f = Math.max(Math.min(max_map, value), min_map) setMapWidth(f); setWalls(new Array(mapHeight).fill().map(() => new Array(f).fill(false))); - setPortals(new Array(mapHeight).fill().map(() => new Array(f).fill(-1))); + setHillGrid(new Array(mapHeight).fill().map(() => new Array(f).fill(0))); setASpawn([-1, -1]) setBSpawn([-1, -1]) - setStartPortal([-1, -1]) - setEndPortal([-1, -1]) + setHillID(1) }; - const handleAppleRateChange = (event) => { + const handlePowerupRateChange = (event) => { const value = event.target.value ? parseInt(event.target.value, 10) : 0; - setAppleRate(Math.max(Math.min(max_apple_rate, value), min_apple_rate)) + setPowerupRate(Math.max(Math.min(max_powerup_rate, value), min_powerup_rate)) }; - const handleAppleNumChange = (event) => { + const handlePowerupNumChange = (event) => { const value = event.target.value ? parseInt(event.target.value, 10) : 0; - setAppleNum(Math.max(Math.min(max_apple_num, value), min_apple_num)) + setPowerupNum(Math.max(Math.min(max_powerup_num, value), min_powerup_num)) }; - const handleShowSnakeStart = (event) => { - setShowSnakeStart(event.target.checked); + const handleShowSpawn = (event) => { + setShowSpawn(event.target.checked); setCanvasRerender(!canvasRerender) }; - const handleStartSizeChange = (event) => { + const handleHillIDChange = (event) => { const value = event.target.value ? parseInt(event.target.value, 10) : 0; - setStartSize(Math.max(Math.min(max_start_size, value), min_start_size)) + setHillID(Math.max(Math.min(max_hill_id, value), min_hill_id)) }; - const handleChangeMapName = (event) => { setMapName(event.target.value) }; @@ -149,11 +133,10 @@ export default function MapBuilder() { const value = event.target.value; setSymmetry(value); setWalls(new Array(mapHeight).fill().map(() => new Array(mapWidth).fill(false))); - setPortals(new Array(mapHeight).fill().map(() => new Array(mapWidth).fill(-1))); + setHillGrid(new Array(mapHeight).fill().map(() => new Array(mapWidth).fill(0))); setASpawn([-1, -1]) setBSpawn([-1, -1]) - setStartPortal([-1, -1]) - setEndPortal([-1, -1]) + setHillID(1) }; @@ -188,50 +171,55 @@ export default function MapBuilder() { ] - let portalList = [] + + + let wallarr = [] for (let i = 0; i < mapHeight; i++) { for (let j = 0; j < mapWidth; j++) { - let portalString = [] - if (portals[i][j] >= 0) { - let othery = Math.floor(portals[i][j] / mapWidth) - let otherx = portals[i][j] % mapWidth - - portalString.push(j) - portalString.push(i) - portalString.push(otherx) - portalString.push(othery) - - portalList.push(portalString.join(",")) - + if (walls[i][j]) { + wallarr.push("1"); + } else { + wallarr.push("0"); } } } + let wallstring = wallarr.join(""); - parts.push(mapWidth.toString() + "," + mapHeight.toString()); - parts.push(aSpawn[0].toString() + "," + aSpawn[1].toString()); - parts.push(bSpawn[0].toString() + "," + bSpawn[1].toString()); - parts.push(startSize.toString()); - parts.push(min_size.toString()); - parts.push(portalList.join("_")); - parts.push(appleRate.toString() + "," + appleNum.toString() + "," + symmetry); - let wallarr = [] + let hillDict = {} + let hillids = [] + let fullHills = [] for (let i = 0; i < mapHeight; i++) { for (let j = 0; j < mapWidth; j++) { - if (walls[i][j]) { - wallarr.push("1"); - } else { - wallarr.push("0"); + if (hillGrid[i][j] > 0) { + const id = hillGrid[i][j] + if(!(id in hillDict)){ + hillDict[id] = [] + } + hillDict[id].push(i+"") + hillDict[id].push(j+"") } } } - let wallstring = wallarr.join(""); + for(const id in hillDict){ + hillids.push(id+"") + fullHills.push(hillDict[id].join(",")) + } + let hillIDstring = hillids.join("") + let hillsString = fullHills.join("_") + + + parts.push(mapHeight.toString() + "," + mapWidth.toString()); + parts.push(aSpawn[0].toString() + "," + aSpawn[1].toString()); + parts.push(bSpawn[0].toString() + "," + bSpawn[1].toString()); parts.push(wallstring); + parts.push(hillIDstring); + parts.push(hillsString); parts.push("0"); - + parts.push(powerupRate.toString() + "," + powerupNum.toString() + "," + symmetry); const generated_string = parts.join("#") return generated_string; @@ -251,155 +239,74 @@ export default function MapBuilder() { } - const setTile = (x, y) => { - if (cellType != GridValues.START_PORTAL && cellType != GridValues.END_PORTAL) { - setStartPortal([-1, -1]) - setEndPortal([-1, -1]) - } + const setTile = (r, c) => { if (cellType == GridValues.EMPTY) { - if (x == aSpawn[0] && y == aSpawn[1]) { + if (r == aSpawn[0] && c == aSpawn[1]) { setASpawn([-1, -1]) setBSpawn([-1, -1]) - } else if (x == bSpawn[0] && y == bSpawn[1]) { + } else if (r == bSpawn[0] && c == bSpawn[1]) { setASpawn([-1, -1]) setBSpawn([-1, -1]) - } else if (walls != null && walls[y][x]) { - - walls[y][x] = false; - const reflection = reflect(x, y); - walls[reflection[1]][reflection[0]] = false; - } else if (portals != null && portals[y][x] >= 0) { - const partnerPortal = portals[y][x] - - const partnerPortalX = partnerPortal % mapWidth - const partnerPortalY = Math.floor(partnerPortal / mapWidth) + } else if (walls != null && walls[r][c]) { - portals[y][x] = -1; - portals[partnerPortalY][partnerPortalX] = -1; + walls[r][c] = false; + const reflection = reflect(r, c); + walls[reflection[0]][reflection[1]] = false; + } else if (hillGrid != null) { + hillGrid[r][c] = 0; } - } else if (cellType == GridValues.SNAKE_A_HEAD) { - const reflection = reflect(x, y); - if (reflection[0] != x || reflection[1] != y) { - if (walls != null && walls[y][x]) { - walls[y][x] = false; - walls[reflection[1]][reflection[0]] = false; - } else if (portals != null && portals[y][x] >= 0) { - const partnerPortal = portals[y][x] - - const partnerPortalX = partnerPortal % mapWidth - const partnerPortalY = Math.floor(partnerPortal / mapWidth) - - portals[y][x] = -1; - portals[partnerPortalY][partnerPortalX] = -1; - } - if (reflection[0] != x || reflection[1] != y) { - setASpawn([x, y]) + } else if (cellType == GridValues.PLAYER_1) { + const reflection = reflect(r, c); + if (reflection[0] != r || reflection[1] != c) { + if (walls != null && walls[r][c]) { + walls[r][c] = false; + walls[reflection[0]][reflection[1]] = false; + } + if (reflection[0] != r || reflection[1] != c) { + setASpawn([r, c]) setBSpawn(reflection) + + console.log(aSpawn) } } - } else if (cellType == GridValues.SNAKE_B_HEAD) { - const reflection = reflect(x, y); - if (reflection[0] != x || reflection[1] != y) { - if (walls != null && walls[y][x]) { - walls[y][x] = false; - walls[reflection[1]][reflection[0]] = false; - } else if (portals != null && portals[y][x] >= 0) { - const partnerPortal = portals[y][x] - - const partnerPortalX = partnerPortal % mapWidth - const partnerPortalY = Math.floor(partnerPortal / mapWidth) - - portals[y][x] = -1; - portals[partnerPortalY][partnerPortalX] = -1; - } - if (reflection[0] != x || reflection[1] != y) { - setBSpawn([x, y]) + } else if (cellType == GridValues.PLAYER_2) { + const reflection = reflect(r, c); + if (reflection[0] != r || reflection[1] != c) { + if (walls != null && walls[c][r]) { + walls[r][c] = false; + walls[reflection[0]][reflection[1]] = false; + } + if (reflection[0] != r || reflection[1] != c) { + setBSpawn([r, c]) setASpawn(reflection) } } } else if (cellType == GridValues.WALL) { - const reflection = reflect(x, y); - if (x == aSpawn[0] && y == aSpawn[1]) { + const reflection = reflect(r, c); + if (r == aSpawn[0] && c == aSpawn[1]) { setASpawn([-1, -1]) setBSpawn([-1, -1]) - } else if (x == bSpawn[0] && y == bSpawn[1]) { + } else if (r == bSpawn[0] && c == bSpawn[1]) { setASpawn([-1, -1]) setBSpawn([-1, -1]) - } else if (portals != null && portals[y][x] >= 0) { - const partnerPortal = portals[y][x] - - const partnerPortalX = partnerPortal % mapWidth - const partnerPortalY = Math.floor(partnerPortal / mapWidth) - - portals[y][x] = -1; - portals[partnerPortalY][partnerPortalX] = -1; + } else if (hillGrid != null) { + hillGrid[r][c] = 0 } - walls[y][x] = true; - walls[reflection[1]][reflection[0]] = true; - } else if (cellType == GridValues.START_PORTAL) { - const reflection = reflect(x, y); - if (x == aSpawn[0] && y == aSpawn[1]) { - setASpawn([-1, -1]) - setBSpawn([-1, -1]) - } else if (x == bSpawn[0] && y == bSpawn[1]) { - setASpawn([-1, -1]) - setBSpawn([-1, -1]) - } else if (walls != null && walls[y][x]) { - walls[y][x] = 0; - walls[reflection[1]][reflection[0]] = 0; - } else if (portals != null && portals[y][x] >= 0) { - const partnerPortal = portals[y][x] - - const partnerPortalX = partnerPortal % mapWidth - const partnerPortalY = Math.floor(partnerPortal / mapWidth) - - portals[y][x] = -1; - portals[partnerPortalY][partnerPortalX] = -1; - - - } - - if (endPortal[0] != -1) { - portals[y][x] = endPortal[1] * mapWidth + endPortal[0] - portals[endPortal[1]][endPortal[0]] = y * mapWidth + x - setEndPortal([-1, -1]) - - } else { - setStartPortal([x, y]) - } - } else if (cellType == GridValues.END_PORTAL) { - const reflection = reflect(x, y); - if (x == aSpawn[0] && y == aSpawn[1]) { - setASpawn([-1, -1]) - setBSpawn([-1, -1]) - } else if (x == bSpawn[0] && y == bSpawn[1]) { - setASpawn([-1, -1]) - setBSpawn([-1, -1]) - } else if (walls != null && walls[y][x] > 0) { - walls[y][x] = 0; - walls[reflection[1]][reflection[0]] = 0; - } else if (portals != null && portals[y][x] >= 0) { - const partnerPortal = portals[y][x] - - const partnerPortalX = partnerPortal % mapWidth - const partnerPortalY = Math.floor(partnerPortal / mapWidth) - - portals[y][x] = -1; - portals[partnerPortalY][partnerPortalX] = -1; - } - if (startPortal[0] != -1) { - portals[y][x] = startPortal[1] * mapWidth + startPortal[0] - portals[startPortal[1]][startPortal[0]] = y * mapWidth + x - setStartPortal([-1, -1]) - - } else { - setEndPortal([x, y]) - } - } + walls[r][c] = true; + walls[reflection[0]][reflection[1]] = true; + } else if (cellType == GridValues.HILL) { + const reflection = reflect(r, c); + if (walls != null && walls[c][r]) { + walls[r][c] = 0; + walls[reflection[0]][reflection[1]] = 0; + } + + hillGrid[r][c] = hillID + } setCanvasRerender(!canvasRerender) } @@ -408,8 +315,8 @@ export default function MapBuilder() { if (walls == null) { setWalls(new Array(mapHeight).fill().map(() => new Array(mapWidth).fill(false))); } - if (portals == null) { - setPortals(new Array(mapHeight).fill().map(() => new Array(mapWidth).fill(-1))); + if (hillGrid == null) { + setHillGrid(new Array(mapHeight).fill().map(() => new Array(mapWidth).fill(0))); } }, []); @@ -467,7 +374,7 @@ export default function MapBuilder() {

Symmetry

- +
@@ -527,16 +434,14 @@ export default function MapBuilder() { >Copy Map String
- { + const drawTile = (r, c, color) => { ctx.fillStyle = color; - ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize); + ctx.fillRect(c * cellSize, r * cellSize, cellSize, cellSize); ctx.strokeStyle = 'black'; - if(x==mouseCellX && y == mouseCellY){ + if(c==mouseCellX && r == mouseCellY){ ctx.lineWidth = 2; } else{ ctx.lineWidth = 0.25; } ctx.strokeRect( - x*cellSize+(ctx.lineWidth/2), - y*cellSize+(ctx.lineWidth/2), + c*cellSize+(ctx.lineWidth/2), + r*cellSize+(ctx.lineWidth/2), cellSize-(ctx.lineWidth), cellSize-(ctx.lineWidth) ); } - const drawPortal = (x, y) => { + const drawHill = (r, c) => { ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; ctx.beginPath(); ctx.arc( - x * cellSize + cellSize / 2, - y * cellSize + cellSize / 2, + c * cellSize + cellSize / 2, + r * cellSize + cellSize / 2, cellSize / 2, 0, Math.PI * 2 @@ -109,12 +111,12 @@ export default function MapVis({ } - const drawSnakeHead = (x, y, color, direction) => { + const drawPlayer = (r, c, color, direction) => { ctx.fillStyle = color; ctx.beginPath(); ctx.arc( - x * cellSize + cellSize / 2, - y * cellSize + cellSize / 2, + c * cellSize + cellSize / 2, + r * cellSize + cellSize / 2, cellSize / 2, 0, Math.PI * 2 @@ -127,80 +129,77 @@ export default function MapVis({ ctx.fillStyle = 'white'; switch(direction) { + case Action.NORTH: + drawEyes(r + 1/3, c + 1/4, r + 2/3, c + 1/4); + break; case Action.EAST: - drawEyes(x + 3/4, y + 1/3, x + 3/4, y + 2/3); + drawEyes(r + 3/4, c + 1/3, r + 3/4, c + 2/3); break; case Action.WEST: - drawEyes(x + 1/4, y + 1/3, x + 1/4, y + 2/3); - break; - case Action.NORTH: - drawEyes(x + 1/3, y + 1/4, x + 2/3, y + 1/4); + drawEyes(r + 1/4, c + 1/3, r + 1/4, c + 2/3); break; case Action.SOUTH: - drawEyes(x + 1/3, y + 3/4, x + 2/3, y + 3/4); + drawEyes(r + 1/3, c + 3/4, r + 2/3, c + 3/4); break; case Action.NORTHEAST: - drawEyes(x + 2/3, y + 1/3, x + 3/4, y + 1/4); + drawEyes(r + 2/3, c + 1/3, r + 3/4, c + 1/4); break; case Action.NORTHWEST: - drawEyes(x + 1/3, y + 1/3, x + 1/4, y + 1/4); + drawEyes(r + 1/3, c + 1/3, r + 1/4, c + 1/4); break; case Action.SOUTHEAST: - drawEyes(x + 2/3, y + 2/3, x + 3/4, y + 3/4); + drawEyes(r + 2/3, c + 2/3, r + 3/4, c + 3/4); break; case Action.SOUTHWEST: - drawEyes(x + 1/3, y + 2/3, x + 1/4, y + 3/4); + drawEyes(r + 1/3, c + 2/3, r + 1/4, c + 3/4); break; } - function drawEyes(x1, y1, x2, y2) { + function drawEyes(r1, c1, r2, c2) { ctx.fillStyle = 'white'; ctx.beginPath(); - ctx.arc(x1 * cellSize, y1 * cellSize, 4, 0, Math.PI * 2); - ctx.arc(x2 * cellSize, y2 * cellSize, 4, 0, Math.PI * 2); + ctx.arc(c1 * cellSize, r1 * cellSize, 4, 0, Math.PI * 2); + ctx.arc(c2 * cellSize, r2 * cellSize, 4, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'black'; ctx.beginPath(); - ctx.arc(x1 * cellSize, y1 * cellSize, 2, 0, Math.PI * 2); - ctx.arc(x2 * cellSize, y2 * cellSize, 2, 0, Math.PI * 2); + ctx.arc(c1 * cellSize, r1 * cellSize, 2, 0, Math.PI * 2); + ctx.arc(c2 * cellSize, r2 * cellSize, 2, 0, Math.PI * 2); ctx.fill(); } } - const drawWall = (x, y) => { + const drawWall = (r, c) => { ctx.fillStyle = 'black'; - ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize); + ctx.fillRect(c * cellSize,r * cellSize, cellSize, cellSize); } - const drawCell = (x, y) => { - if(walls != null && walls[y][x]){ - drawWall(x, y); - } - else if(portals != null && portals[y][x] >= 0){ - drawPortal(x, y) - + const drawCell = (r, c) => { + if(hillGrid != null && hillGrid[r][c] != 0){ + drawHill(r, c) } - else if((x == startPortal[0] && y == startPortal[1])||(x == endPortal[0] && y == endPortal[1])){ - drawPortal(x, y) + + if(walls != null && walls[r][c]){ + drawWall(r, c); } - else if(showSnakeStart && x == aSpawn[0] && y == aSpawn[1]){ - drawSnakeHead(x, y, 'green', Action.NORTH); + else if(showSpawn && r == aSpawn[0] && c == aSpawn[1]){ + drawPlayer(r, c, 'green', Action.NORTH); } - else if(showSnakeStart && x == bSpawn[0] && y == bSpawn[1]){ - drawSnakeHead(x, y, 'blue', Action.NORTH); + else if(showSpawn && r == bSpawn[0] && c == bSpawn[1]){ + drawPlayer(r, c, 'blue', Action.NORTH); } } - for (let y = 0; y < mapHeight; y++) { - for (let x = 0; x < mapWidth; x++) { - drawTile(x, y, '#B19E4E'); + for (let r = 0; r < mapHeight; r++) { + for (let c = 0; c < mapWidth; c++) { + drawTile(r, c, '#B19E4E'); } } - for (let y = 0; y < mapHeight; y++) { - for (let x = 0; x < mapWidth; x++) { - drawCell(x, y); + for (let r = 0; r < mapHeight; r++) { + for (let c = 0; c < mapWidth; c++) { + drawCell(r, c); } } return () => { @@ -212,12 +211,10 @@ export default function MapVis({ }, [ aSpawn, bSpawn, - startPortal, - endPortal, mapHeight, mapWidth, walls, - portals, + hillGrid, mouseCellX, mouseCellY, rerender diff --git a/src/components/MapSettings.js b/src/components/MapSettings.js index c276802..dd41115 100644 --- a/src/components/MapSettings.js +++ b/src/components/MapSettings.js @@ -1,9 +1,9 @@ function MapSettings({ mapHeight, handleHeightChange, mapWidth, handleWidthChange, - appleRate, handleAppleRateChange, - appleNum, handleAppleNumChange, - startSize, handleStartSizeChange + powerupRate, handlePowerupRateChange, + powerupNum, handlePowerupNumChange, + hillID, handleHillIDChange }) { return (
@@ -33,38 +33,38 @@ function MapSettings({
- +
- +
- +
diff --git a/src/components/MatchPlayer.js b/src/components/MatchPlayer.js index 4bb3025..ae2b9d0 100644 --- a/src/components/MatchPlayer.js +++ b/src/components/MatchPlayer.js @@ -10,8 +10,6 @@ import GameOutputs from './GameOutputs'; import PlayerStats from './PlayerStats'; import { Button } from '@/components/ui/button'; import { Bot } from 'lucide-react'; -import { match } from 'assert'; - const path = require('path'); @@ -26,6 +24,7 @@ function MatchPlayer() { const [engineOutput, setEngineOutput] = useState(null); const [map, setMap] = useState(null); const [matchInfo, setMatchInfo] = useState(null) + const [isMatchRunning, setIsMatchRunning] = useState(false); const botCount = (bot1File && bot2File ? 2 : bot1File || bot2File ? 1 : 0); const canStart = bot1File && bot2File && map; @@ -48,11 +47,12 @@ function MatchPlayer() { const handleSetMap = (value) => { setMap(value) + console.log(value) - let match_states = new Array(1).fill(null); - match_states[0] = getMap(value) - setMatchStates(match_states); - setCurrentMatchStateIndex(0); + // let match_states = new Array(1).fill(null); + // match_states[0] = getMap(value) + // setMatchStates(match_states); + // setCurrentMatchStateIndex(0); setMatchInfo(null) } @@ -69,19 +69,54 @@ function MatchPlayer() { }; const handleBattleStart = () => { + if (isMatchRunning) { + // Stop the match + window.electron.sendTCPInterrupt(); + } else { + // Start the match setBot1File(bot1File); setBot2File(bot2File); setShouldPlayMatch(true); + setIsMatchRunning(true); + } +} + + const handleStdOutData = (chunk) => { + console.log("stdout"); + console.log(chunk); + } + + const handleStdOutDataFull = (fullOutput) => { + console.log("stdoutfull"); + console.log(fullOutput); + } + + const handleErrOutData = (chunk) => { + console.log("errout"); + console.log(chunk); + } + + const handleErrOutDataFull = (fullOutput) => { + console.log("erroutfull"); + console.log(fullOutput); + } + + const handleTcpData = (data) => { + console.log("tcpdata"); + console.log(data); } - - const tcpJSONCallback = () => { + const handleTcpMessage = (json) => { + console.log("tcpmessage"); + console.log(json); } - const tcpStatusCallback = () => { - + const handleTcpStatus = (status) => { + console.log("tcpstatus"); + console.log(status); } + useEffect(() => { let interval; if (isPlaying) { @@ -121,14 +156,37 @@ function MatchPlayer() { '--output_dir', `"${resultFilePath}"` ]; - - const result = await window.electron.runPythonScript(scriptArgs); + + // register handlers + // Register handlers and get cleanup functions + const cleanupTcpData = window.electron.onTcpData(handleTcpData); + const cleanupTcpJson = window.electron.onTcpJson(handleTcpMessage); + const cleanupTcpStatus = window.electron.onTcpStatus(handleTcpStatus); + const cleanupOutput = window.electron.onStreamOutput(handleStdOutData); + const cleanupOutputFull = window.electron.onStreamOutputFull(handleStdOutDataFull); + const cleanupError = window.electron.onStreamError(handleErrOutData); + const cleanupErrorFull = window.electron.onStreamErrorFull(handleErrOutDataFull); + + try { + setEngineOutput(await window.electron.runPythonScript(scriptArgs)); + setIsMatchRunning(false); + + } finally { + setIsMatchRunning(false); + cleanupTcpData(); + cleanupTcpJson(); + cleanupTcpStatus(); + cleanupOutput(); + cleanupOutputFull(); + cleanupError(); + cleanupErrorFull(); + } try { const resultFileContent = await window.electron.readFile(resultFilePath); const matchLog = JSON.parse(resultFileContent); await window.electron.copyMatch(resultFilePath, num); - await window.electron.storeSet("numMatches", (num + 1) % 100000) + await window.electron.storeSet("numMatches", (num + 1) % 1000000) const m = await processData(matchLog); setMatchStates(m.match_states); @@ -157,7 +215,7 @@ function MatchPlayer() {
- Turn #:{currentMatchStateIndex} + Turn #:{currentMatchStateIndex}
@@ -171,15 +229,17 @@ function MatchPlayer() { + disabled={!canStart && !isMatchRunning} + onClick={handleBattleStart} + > + {isMatchRunning ? 'Stop Match' : 'Start Battle'} + Show Spawn Location