Skip to content
Snippets Groups Projects
Verified Commit 00a9aed8 authored by Alex Reinhardt's avatar Alex Reinhardt
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
# Logs
logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Runtime data
pids
_.pid
_.seed
\*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
\*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
\*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
\*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.\*
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store
bun.lockb
\ No newline at end of file
# hackatron
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run main.ts
```
This project was created using `bun init` in bun v1.0.6. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
main.ts 0 → 100644
import {Socket} from 'bun'
let field: number[][] = []
type Player = {name: string, x: number, y: number, self: boolean}
let players: Map<number, Player> = new Map();
let self = -1
await Bun.connect({
hostname: 'game.hackatron.de',
port: 4000,
socket: {
data,
open: (socket) => {
console.info('OPEN')
},
close: (socket) => {
console.info('CLOSE')
},
drain: (socket) => {
console.info('DRAIN')
},
error: (socket, error) => {
console.error(error)
},
connectError: (socket, error) => {
console.error(error)
},
end: (socket) => {
console.info('END')
},
timeout: (socket) => {
console.info('TIMEOUT')
}
}
})
function send(socket: Socket, ...args: string[]) {
socket.write(args.join('|') + '\n')
}
function sendJoin(socket: Socket, name: string, password: string) {
send(socket, 'join', name, password)
console.info(`JOIN ${name} [${password}]`)
}
enum Direction {
UP = 'up',
DOWN = 'down',
LEFT = 'left',
RIGHT = 'right'
}
function sendMove(socket: Socket, direction: Direction) {
send(socket, 'move', direction)
console.info(`MOVE ${direction.toUpperCase()}`)
}
function data(socket: Socket, buffer: Buffer) {
dataString(socket, buffer.toString())
}
function dataString(socket: Socket, data: string) {
for(let line of data.split('\n')) {
if(line.length === 0) continue
command(socket, line.split('|'))
}
}
function command(socket: Socket, args: string[]) {
switch(args[0]) {
case 'motd': motd(socket, args[1]); break
case 'error': error(socket, args[1]); break
case 'game': game(socket, parseInt(args[1]), parseInt(args[2]), parseInt(args[3])); break
case 'pos': pos(socket, parseInt(args[1]), parseInt(args[2]), parseInt(args[3])); break
case 'player': player(socket, parseInt(args[1]), args[2]); break
case 'tick': tick(socket); break
case 'die': die(socket, parseInt(args[1])); break
case 'win': end(socket, true, parseInt(args[1]), parseInt(args[2])); break
case 'lose': end(socket, false, parseInt(args[1]), parseInt(args[2])); break
default: console.error('UNKNOWN PACKET'); console.error(args)
}
}
function motd(socket: Socket, message: string) {
console.info(`MOTD: ${message}`)
sendJoin(socket, 'Meow :3', 'MeowMeow')
}
function error(socket: Socket, error: string) {
console.error(error)
}
function game(socket: Socket, width: number, height: number, id: number) {
console.info(`GAME: ${width}x${height} (${id})`)
field = []
for(let x = 0; x < width; x++) {
field[x] = []
for(let y = 0; y < height; y++) {
field[x][y] = -1
}
}
players = new Map()
self = id
}
function pos(socket: Socket, id: number, x: number, y: number) {
console.info(`POS of ${id}: ${x}, ${y}`)
field[x][y] = id
let player = players.get(id) ?? {name: 'Unknown', x: -1, y: -1, self: false}
player.x = x
player.y = y
players.set(id, player)
}
function player(socket: Socket, id: number, name: string) {
console.info(`PLAYER ${id}: ${name}`)
players.set(id, ({name, x: -1, y: -1, self: name == 'Meow :3'}))
}
function tick(socket: Socket) {
console.info(`TICK`)
strategy(socket)
}
function die(socket: Socket, id: number) {
console.info(`DIE ${id}`)
for(let x = 0; x < field.length; x++) {
for(let y = 0; y < field[x].length; y++) {
if(field[x][y] === id) field[x][y] = -1
}
}
}
function end(socket: Socket, win: boolean, wins: number, loses: number) {
console.info(`END: ${win ? 'WIN' : 'LOSE'} (${wins}:${loses})`)
}
function flatPos(x: number, y: number) {
return x + y * field.length
}
function matrixPos(pos: number) {
return {
x: pos % field.length,
y: Math.floor(pos / field.length)
}
}
function leftFrom(x: number, y: number) {
if(x == 0) return { x: field.length - 1, y }
else return { x: x - 1, y }
}
function rightFrom(x: number, y: number) {
if(x == field.length - 1) return { x: 0, y }
else return { x: x + 1, y }
}
function upFrom(x: number, y: number) {
if(y == 0) return { x, y: field[x].length - 1 }
else return { x, y: y - 1 }
}
function downFrom(x: number, y: number) {
if(y == field[x].length - 1) return { x, y: 0 }
else return { x, y: y + 1 }
}
function spaceAround(lField: number[][], x: number, y: number) {
let value = 0
let queue: Set<number> = new Set()
let visited: Set<number> = new Set()
queue.add(flatPos(x, y))
while(queue.size != 0) {
let pos = queue.values().next().value
queue.delete(pos)
visited.add(pos)
let {x, y} = matrixPos(pos)
if(lField[x][y] !== -1) continue
value++
let leftPos = leftFrom(x, y)
let left = flatPos(leftPos.x, leftPos.y)
if(!visited.has(left)) queue.add(left)
let rightPos = rightFrom(x, y)
let right = flatPos(rightPos.x, rightPos.y)
if(!visited.has(right)) queue.add(right)
let upPos = upFrom(x, y)
let up = flatPos(upPos.x, upPos.y)
if(!visited.has(up)) queue.add(up)
let downPos = downFrom(x, y)
let down = flatPos(downPos.x, downPos.y)
if(!visited.has(down)) queue.add(down)
}
return value
}
function collisionPossible(self: {x: number, y: number}, other: {x: number, y: number}) {
let otherUp = upFrom(other.x, other.y)
let otherDown = downFrom(other.x, other.y)
let otherLeft = leftFrom(other.x, other.y)
let otherRight = rightFrom(other.x, other.y)
return (self.x == otherUp.x && self.y == otherUp.y)
|| (self.x == otherDown.x && self.y == otherDown.y)
|| (self.x == otherLeft.x && self.y == otherLeft.y)
|| (self.x == otherRight.x && self.y == otherRight.y)
}
function anyCollisionPossible(x: number, y: number) {
for(let player of players.values()) {
if(player.self) continue
if(collisionPossible({x, y}, {x: player.x, y: player.y})) return true
}
return false
}
function strategy(socket: Socket) {
let x = players.get(self)?.x ?? -1
let y = players.get(self)?.y ?? -1
if(x === -1 || y === -1) return
let upPos = upFrom(x, y)
let spaceAroundTop = spaceAround(field, upPos.x, upPos.y)
let up = 0
let wrap = false
for(let i = y-1; i >= 0; i--) {
if(field[x][i] !== -1) break
up++
if(i == 0) wrap = true
}
if(wrap || y == 0) {
for(let i = field[x].length - 1; i > y; i--) {
if(field[x][i] !== -1) break
up++
}
}
let downPos = downFrom(x, y)
let spaceAroundBottom = spaceAround(field, downPos.x, downPos.y)
let down = 0
wrap = false
for(let i = y+1; i < field[x].length; i++) {
if(field[x][i] !== -1) break
down++
if(i == field[x].length - 1) wrap = true
}
if(wrap || y == field[x].length - 1) {
for(let i = 0; i < y; i++) {
if(field[x][i] !== -1) break
down++
}
}
let leftPos = leftFrom(x, y)
let spaceAroundLeft = spaceAround(field, leftPos.x, leftPos.y)
let left = 0
wrap = false
for(let i = x-1; i >= 0; i--) {
if(field[i][y] !== -1) break
left++
if(i == 0) wrap = true
}
if(wrap || x == 0) {
for(let i = field.length - 1; i > x; i--) {
if(field[i][y] !== -1) break
left++
}
}
let rightPos = rightFrom(x, y)
let spaceAroundRight = spaceAround(field, rightPos.x, rightPos.y)
let right = 0
wrap = false
for(let i = x+1; i < field.length; i++) {
if(field[i][y] !== -1) break
right++
if(i == field.length - 1) wrap = true
}
if(wrap || x == field.length - 1) {
for(let i = 0; i < x; i++) {
if(field[i][y] !== -1) break
right++
}
}
let maxSpaceAround = Math.max(spaceAroundTop, spaceAroundBottom, spaceAroundLeft, spaceAroundRight)
let hasSpaceLeft = spaceAroundLeft == maxSpaceAround
let hasSpaceRight = spaceAroundRight == maxSpaceAround
let hasSpaceTop = spaceAroundTop == maxSpaceAround
let hasSpaceBottom = spaceAroundBottom == maxSpaceAround
let dir = [
{ dir: Direction.UP, hasSpace: hasSpaceTop, space: spaceAroundTop, distance: up, possibleCollision: anyCollisionPossible(upPos.x, upPos.y) },
{ dir: Direction.DOWN, hasSpace: hasSpaceBottom, space: spaceAroundBottom, distance: down, possibleCollision: anyCollisionPossible(downPos.x, downPos.y) },
{ dir: Direction.LEFT, hasSpace: hasSpaceLeft, space: spaceAroundLeft, distance: left, possibleCollision: anyCollisionPossible(leftPos.x, leftPos.y) },
{ dir: Direction.RIGHT, hasSpace: hasSpaceRight, space: spaceAroundRight, distance: right, possibleCollision: anyCollisionPossible(rightPos.x, rightPos.y) }
]
dir.sort((a, b) => {
return b.distance - a.distance - (b.possibleCollision ? 100 : 0) + (a.possibleCollision ? 100 : 0)
})
console.log(dir)
if(dir[0].hasSpace) {
sendMove(socket, dir[0].dir)
} else if(dir[1].hasSpace) {
sendMove(socket, dir[1].dir)
} else if(dir[2].hasSpace) {
sendMove(socket, dir[2].dir)
} else if(dir[3].hasSpace) {
sendMove(socket, dir[3].dir)
}
}
\ No newline at end of file
{
"name": "hackatron",
"module": "main.ts",
"devDependencies": {
"bun-types": "latest"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"type": "module"
}
\ No newline at end of file
{
"compilerOptions": {
"lib": ["ESNext"],
"module": "esnext",
"target": "esnext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
"noEmit": true,
"composite": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "react-jsx",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"types": [
"bun-types" // add Bun global
]
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment