Compare commits
	
		
			No commits in common. "fc2c8ac1aa757181b467727eb574ffae480d9826" and "cd93ccb063b7da8d595bf8bc53ed72291d4fb751" have entirely different histories.
		
	
	
		
			fc2c8ac1aa
			...
			cd93ccb063
		
	
		
					 12 changed files with 1 additions and 774 deletions
				
			
		
							
								
								
									
										1
									
								
								asteracer-typescript/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								asteracer-typescript/.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -1 +0,0 @@ | ||||||
| node_modules |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| Použití: |  | ||||||
| 
 |  | ||||||
| - Instalujte si Node >= 22 (`nvm use 22` pokud máte nvm) (je potřeba pro přímé spouštění typescriptu bez kompilátoru) |  | ||||||
| - `npm install` |  | ||||||
| - `npm run app [název souboru mapy] [název souboru s instrukcemi]` |  | ||||||
|  | @ -1,34 +0,0 @@ | ||||||
| import { readFileSync } from "fs"; |  | ||||||
| import { checkInstructions, simulate } from "./src/asteracer/simulation"; |  | ||||||
| import { parseInstructions, parseMap } from "./src/asteracer/parse_map"; |  | ||||||
| 
 |  | ||||||
| const worldFileName = process.argv[2]; |  | ||||||
| const instructionsFileName = process.argv[3]; |  | ||||||
| 
 |  | ||||||
| if (!worldFileName || !instructionsFileName) { |  | ||||||
|     console.log("Použití: npm run app [název souboru mapy] [název souboru s instrukcemi]"); |  | ||||||
|     process.exit(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const worldFileText = readFileSync(worldFileName, { encoding: 'utf8', flag: 'r' }); |  | ||||||
| const instructionsFileText = readFileSync(instructionsFileName, { encoding: 'utf8', flag: 'r' }); |  | ||||||
| 
 |  | ||||||
| const world = parseMap(worldFileText); |  | ||||||
| const instructions = parseInstructions(instructionsFileText); |  | ||||||
| 
 |  | ||||||
| try { |  | ||||||
|     checkInstructions(instructions); |  | ||||||
| } catch (e) { |  | ||||||
|     console.error(e.message); |  | ||||||
|     process.exit(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const simulationResult = await simulate(world, instructions); |  | ||||||
| 
 |  | ||||||
| if (simulationResult.messages) { |  | ||||||
|     for (const msg of simulationResult.messages) { |  | ||||||
|         console.log(msg); |  | ||||||
|     } |  | ||||||
| } else { |  | ||||||
|     console.log("Závod úspěšně dokončen!"); |  | ||||||
| } |  | ||||||
							
								
								
									
										236
									
								
								asteracer-typescript/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										236
									
								
								asteracer-typescript/package-lock.json
									
									
									
										generated
									
									
									
								
							|  | @ -1,236 +0,0 @@ | ||||||
| { |  | ||||||
|   "name": "asteracer-simulation", |  | ||||||
|   "version": "1.0.0", |  | ||||||
|   "lockfileVersion": 3, |  | ||||||
|   "requires": true, |  | ||||||
|   "packages": { |  | ||||||
|     "": { |  | ||||||
|       "name": "asteracer-simulation", |  | ||||||
|       "version": "1.0.0", |  | ||||||
|       "license": "ISC", |  | ||||||
|       "devDependencies": { |  | ||||||
|         "@types/node": "^22.13.13", |  | ||||||
|         "ts-node": "^10.9.2", |  | ||||||
|         "typescript": "^5.7.3" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@cspotcode/source-map-support": { |  | ||||||
|       "version": "0.8.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", |  | ||||||
|       "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT", |  | ||||||
|       "dependencies": { |  | ||||||
|         "@jridgewell/trace-mapping": "0.3.9" |  | ||||||
|       }, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=12" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@jridgewell/resolve-uri": { |  | ||||||
|       "version": "3.1.2", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", |  | ||||||
|       "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT", |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=6.0.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@jridgewell/sourcemap-codec": { |  | ||||||
|       "version": "1.5.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", |  | ||||||
|       "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/@jridgewell/trace-mapping": { |  | ||||||
|       "version": "0.3.9", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", |  | ||||||
|       "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT", |  | ||||||
|       "dependencies": { |  | ||||||
|         "@jridgewell/resolve-uri": "^3.0.3", |  | ||||||
|         "@jridgewell/sourcemap-codec": "^1.4.10" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@tsconfig/node10": { |  | ||||||
|       "version": "1.0.11", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", |  | ||||||
|       "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/@tsconfig/node12": { |  | ||||||
|       "version": "1.0.11", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", |  | ||||||
|       "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/@tsconfig/node14": { |  | ||||||
|       "version": "1.0.3", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", |  | ||||||
|       "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/@tsconfig/node16": { |  | ||||||
|       "version": "1.0.4", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", |  | ||||||
|       "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/@types/node": { |  | ||||||
|       "version": "22.13.13", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz", |  | ||||||
|       "integrity": "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT", |  | ||||||
|       "dependencies": { |  | ||||||
|         "undici-types": "~6.20.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/acorn": { |  | ||||||
|       "version": "8.14.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", |  | ||||||
|       "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT", |  | ||||||
|       "bin": { |  | ||||||
|         "acorn": "bin/acorn" |  | ||||||
|       }, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=0.4.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/acorn-walk": { |  | ||||||
|       "version": "8.3.4", |  | ||||||
|       "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", |  | ||||||
|       "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT", |  | ||||||
|       "dependencies": { |  | ||||||
|         "acorn": "^8.11.0" |  | ||||||
|       }, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=0.4.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/arg": { |  | ||||||
|       "version": "4.1.3", |  | ||||||
|       "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", |  | ||||||
|       "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/create-require": { |  | ||||||
|       "version": "1.1.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", |  | ||||||
|       "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/diff": { |  | ||||||
|       "version": "4.0.2", |  | ||||||
|       "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", |  | ||||||
|       "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "BSD-3-Clause", |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=0.3.1" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/make-error": { |  | ||||||
|       "version": "1.3.6", |  | ||||||
|       "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", |  | ||||||
|       "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "ISC" |  | ||||||
|     }, |  | ||||||
|     "node_modules/ts-node": { |  | ||||||
|       "version": "10.9.2", |  | ||||||
|       "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", |  | ||||||
|       "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT", |  | ||||||
|       "dependencies": { |  | ||||||
|         "@cspotcode/source-map-support": "^0.8.0", |  | ||||||
|         "@tsconfig/node10": "^1.0.7", |  | ||||||
|         "@tsconfig/node12": "^1.0.7", |  | ||||||
|         "@tsconfig/node14": "^1.0.0", |  | ||||||
|         "@tsconfig/node16": "^1.0.2", |  | ||||||
|         "acorn": "^8.4.1", |  | ||||||
|         "acorn-walk": "^8.1.1", |  | ||||||
|         "arg": "^4.1.0", |  | ||||||
|         "create-require": "^1.1.0", |  | ||||||
|         "diff": "^4.0.1", |  | ||||||
|         "make-error": "^1.1.1", |  | ||||||
|         "v8-compile-cache-lib": "^3.0.1", |  | ||||||
|         "yn": "3.1.1" |  | ||||||
|       }, |  | ||||||
|       "bin": { |  | ||||||
|         "ts-node": "dist/bin.js", |  | ||||||
|         "ts-node-cwd": "dist/bin-cwd.js", |  | ||||||
|         "ts-node-esm": "dist/bin-esm.js", |  | ||||||
|         "ts-node-script": "dist/bin-script.js", |  | ||||||
|         "ts-node-transpile-only": "dist/bin-transpile.js", |  | ||||||
|         "ts-script": "dist/bin-script-deprecated.js" |  | ||||||
|       }, |  | ||||||
|       "peerDependencies": { |  | ||||||
|         "@swc/core": ">=1.2.50", |  | ||||||
|         "@swc/wasm": ">=1.2.50", |  | ||||||
|         "@types/node": "*", |  | ||||||
|         "typescript": ">=2.7" |  | ||||||
|       }, |  | ||||||
|       "peerDependenciesMeta": { |  | ||||||
|         "@swc/core": { |  | ||||||
|           "optional": true |  | ||||||
|         }, |  | ||||||
|         "@swc/wasm": { |  | ||||||
|           "optional": true |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/typescript": { |  | ||||||
|       "version": "5.8.2", |  | ||||||
|       "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", |  | ||||||
|       "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "Apache-2.0", |  | ||||||
|       "bin": { |  | ||||||
|         "tsc": "bin/tsc", |  | ||||||
|         "tsserver": "bin/tsserver" |  | ||||||
|       }, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=14.17" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/undici-types": { |  | ||||||
|       "version": "6.20.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", |  | ||||||
|       "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/v8-compile-cache-lib": { |  | ||||||
|       "version": "3.0.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", |  | ||||||
|       "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT" |  | ||||||
|     }, |  | ||||||
|     "node_modules/yn": { |  | ||||||
|       "version": "3.1.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", |  | ||||||
|       "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", |  | ||||||
|       "dev": true, |  | ||||||
|       "license": "MIT", |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=6" |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| { |  | ||||||
|   "name": "asteracer-simulation", |  | ||||||
|   "version": "1.0.0", |  | ||||||
|   "description": "", |  | ||||||
|   "main": "index.js", |  | ||||||
|   "type": "module", |  | ||||||
|   "scripts": { |  | ||||||
|     "app": "node --experimental-strip-types --experimental-specifier-resolution=node --loader ts-node/esm ./index.ts" |  | ||||||
|   }, |  | ||||||
|   "author": "", |  | ||||||
|   "license": "ISC", |  | ||||||
|   "devDependencies": { |  | ||||||
|     "@types/node": "^22.13.13", |  | ||||||
|     "ts-node": "^10.9.2", |  | ||||||
|     "typescript": "^5.7.3" |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,72 +0,0 @@ | ||||||
| import { Aabb, CircleId, Point } from "./types"; |  | ||||||
| 
 |  | ||||||
| export interface IGrid { |  | ||||||
|     //queryAlongLine(a: Point, b: Point, radius: number): Circle[];
 |  | ||||||
|     queryCircle(p: Point, radius: number): CircleId[]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Stupid "acceleration" structure that just iterates over everything. |  | ||||||
|  */ |  | ||||||
| export class Grid implements IGrid { |  | ||||||
|     private _circles: CircleId[]; |  | ||||||
| 
 |  | ||||||
|     constructor(items: CircleId[]) { |  | ||||||
|         this._circles = items; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public queryAlongLine(a: Point, b: Point, radius: number): CircleId[] { |  | ||||||
|         const intersections: CircleId[] = []; |  | ||||||
| 
 |  | ||||||
|         for (const c of this._circles) { |  | ||||||
|             const dist = pointLineSegmentDistance(a, b, c); |  | ||||||
|             if (dist < (c.radius + radius) * 1.5) { |  | ||||||
|                 intersections.push(c); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return intersections; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public queryCircle(p: Point, radius: number): CircleId[] { |  | ||||||
|         const intersections: CircleId[] = []; |  | ||||||
| 
 |  | ||||||
|         for (let i = 0; i < this._circles.length; i++) { |  | ||||||
|             const c = this._circles[i]; |  | ||||||
|             const dx = (c.x - p.x) | 0; |  | ||||||
|             const dy = (c.y - p.y) | 0; |  | ||||||
|             const distSq = dx * dx + dy * dy; |  | ||||||
|             const threshold = (radius + c.radius + 100) | 0; |  | ||||||
|             if (distSq <= threshold * threshold) { |  | ||||||
|                 intersections.push(c); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return intersections; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function distanceF64(a: Point, b: Point): number { |  | ||||||
|     const dx = a.x - b.x; |  | ||||||
|     const dy = a.y - b.y; |  | ||||||
|     return Math.sqrt(dx * dx + dy * dy); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function pointLineSegmentDistance(lineA: Point, lineB: Point, point: Point): number { |  | ||||||
|     const dirX = lineB.x - lineA.x; |  | ||||||
|     const dirY = lineB.y - lineA.y; |  | ||||||
|     const relativePointX = point.x - lineA.x; |  | ||||||
|     const relativePointY = point.y - lineA.y; |  | ||||||
|     const lineLength = Math.sqrt(dirX * dirX + dirY * dirY); |  | ||||||
| 
 |  | ||||||
|     const distAlongLineClosestPoint = (dirX * relativePointX + dirY * relativePointY) / lineLength; |  | ||||||
|     const clampedDistAlongLineClosestPoint = Math.min(Math.max(distAlongLineClosestPoint, 0), lineLength); |  | ||||||
| 
 |  | ||||||
|     const closest = { |  | ||||||
|         x: lineA.x + dirX * clampedDistAlongLineClosestPoint, |  | ||||||
|         y: lineA.y + dirY * clampedDistAlongLineClosestPoint, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const distClosest = distanceF64(closest, point); |  | ||||||
|     return distClosest; |  | ||||||
| } |  | ||||||
|  | @ -1,103 +0,0 @@ | ||||||
| import { Point, World } from "./types"; |  | ||||||
| 
 |  | ||||||
| class Parser { |  | ||||||
|     private _split: string[]; |  | ||||||
|     private _index: number = 0; |  | ||||||
| 
 |  | ||||||
|     constructor(file: string) { |  | ||||||
|         this._split = file.split(/\s+/); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public readInt(): number { |  | ||||||
|         const int = parseInt(this._split[this._index]) | 0; |  | ||||||
|         this._index++; |  | ||||||
|         return int; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public end(): boolean { |  | ||||||
|         return this._index === this._split.length - 1; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function parseMap(file: string): World { |  | ||||||
|     const input = new Parser(file); |  | ||||||
| 
 |  | ||||||
|     const world: World = { |  | ||||||
|         shipStartX: input.readInt(), |  | ||||||
|         shipStartY: input.readInt(), |  | ||||||
|         shipRadius: input.readInt(), |  | ||||||
|         minX: input.readInt(), |  | ||||||
|         minY: input.readInt(), |  | ||||||
|         maxX: input.readInt(), |  | ||||||
|         maxY: input.readInt(), |  | ||||||
|         asteroids: [], |  | ||||||
|         goals: [], |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const asteroidCount = input.readInt(); |  | ||||||
| 
 |  | ||||||
|     for (let i = 0; i < asteroidCount; i++) { |  | ||||||
|         world.asteroids.push({ |  | ||||||
|             x: input.readInt(), |  | ||||||
|             y: input.readInt(), |  | ||||||
|             radius: input.readInt(), |  | ||||||
|             id: i, |  | ||||||
|             type: "asteroid", |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const goalCount = input.readInt(); |  | ||||||
| 
 |  | ||||||
|     for (let i = 0; i < goalCount; i++) { |  | ||||||
|         world.goals.push({ |  | ||||||
|             x: input.readInt(), |  | ||||||
|             y: input.readInt(), |  | ||||||
|             radius: input.readInt(), |  | ||||||
|             id: i, |  | ||||||
|             type: "goal", |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return world; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function parseInstructions(file: string): Point[] { |  | ||||||
|     const input = new Parser(file); |  | ||||||
| 
 |  | ||||||
|     const instructions: Point[] = []; |  | ||||||
| 
 |  | ||||||
|     const instructionCount = input.readInt(); |  | ||||||
| 
 |  | ||||||
|     for (let i = 0; i < instructionCount; i++) { |  | ||||||
|         if (input.end()) { |  | ||||||
|             throw new Error("V souboru je méně instrukcí, než kolik udává jeho první řádek!"); |  | ||||||
|         } |  | ||||||
|         instructions.push({ |  | ||||||
|             x: input.readInt(), |  | ||||||
|             y: input.readInt(), |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return instructions; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function parseLog(file: string): [Point[], Point[]] { |  | ||||||
|     const expectedPositions: Point[] = []; |  | ||||||
|     const expectedVelocity: Point[] = []; |  | ||||||
| 
 |  | ||||||
|     const input = new Parser(file); |  | ||||||
| 
 |  | ||||||
|     while(!input.end()) { |  | ||||||
|         expectedPositions.push({ |  | ||||||
|             x: input.readInt(), |  | ||||||
|             y: input.readInt(), |  | ||||||
|         }); |  | ||||||
|         expectedVelocity.push({ |  | ||||||
|             x: input.readInt(), |  | ||||||
|             y: input.readInt(), |  | ||||||
|         }); |  | ||||||
|         input.readInt(); // ignore goals
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return [expectedPositions, expectedVelocity]; |  | ||||||
| } |  | ||||||
|  | @ -1,241 +0,0 @@ | ||||||
| import { timeout } from "../util"; |  | ||||||
| import { Grid, IGrid } from "./grid"; |  | ||||||
| import { Aabb, CircleId, Point, SimulationResult, TickResult, World } from "./types"; |  | ||||||
| 
 |  | ||||||
| const dragSlowdownFraction = [9, 10]; |  | ||||||
| const collisionSlowdownFraction = [1, 2]; |  | ||||||
| const maxCollisionSubticks = 5; |  | ||||||
| 
 |  | ||||||
| function tick(gridAsteroids: IGrid, gridGoals: IGrid, worldAabb: Aabb, shipRadius: number, shipPositionStart: Point, shipVelocityStart: Point, instruction: Point): TickResult { |  | ||||||
|     let velocityX = shipVelocityStart.x | 0; |  | ||||||
|     let velocityY = shipVelocityStart.y | 0; |  | ||||||
| 
 |  | ||||||
|     const r = shipRadius | 0; |  | ||||||
| 
 |  | ||||||
|     // Zpomalení
 |  | ||||||
|     velocityX = ((velocityX * (dragSlowdownFraction[0] | 0)) / (dragSlowdownFraction[1] | 0)) | 0; |  | ||||||
|     velocityY = ((velocityY * (dragSlowdownFraction[0] | 0)) / (dragSlowdownFraction[1] | 0)) | 0; |  | ||||||
| 
 |  | ||||||
|     // Instrukce
 |  | ||||||
|     velocityX += instruction.x | 0; |  | ||||||
|     velocityY += instruction.y | 0; |  | ||||||
| 
 |  | ||||||
|     // Posun
 |  | ||||||
|     let positionX = (shipPositionStart.x | 0) + velocityX; |  | ||||||
|     let positionY = (shipPositionStart.y | 0) + velocityY; |  | ||||||
| 
 |  | ||||||
|     // Kolize
 |  | ||||||
|     let hadAnyCollision = false; |  | ||||||
| 
 |  | ||||||
|     for (let subtick = 0; subtick < maxCollisionSubticks; subtick++) { |  | ||||||
|         let collidedThisSubtick = false; |  | ||||||
| 
 |  | ||||||
|         const asteroidCandidates = gridAsteroids.queryCircle({ |  | ||||||
|             x: positionX, |  | ||||||
|             y: positionY, |  | ||||||
|         }, r); |  | ||||||
| 
 |  | ||||||
|         for (let i = 0; i < asteroidCandidates.length; i++) { |  | ||||||
|             const c = asteroidCandidates[i]; |  | ||||||
|             const dx = (positionX - c.x) | 0; // od středu asteroidu ke středu lodi
 |  | ||||||
|             const dy = (positionY - c.y) | 0; |  | ||||||
|             const distSq = dx * dx + dy * dy; |  | ||||||
|             const dist = Math.sqrt(distSq) | 0; |  | ||||||
|             const threshold = (r + c.radius) | 0; |  | ||||||
|             if (dist > threshold) { |  | ||||||
|                 // Kolize nenastala
 |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Kolidujeme
 |  | ||||||
|             const pushBy = (c.radius + r) - dist; |  | ||||||
|             positionX += (((pushBy * dx) | 0) / dist) | 0; |  | ||||||
|             positionY += (((pushBy * dy) | 0) / dist) | 0; |  | ||||||
| 
 |  | ||||||
|             collidedThisSubtick = true; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Kolize s okraji
 |  | ||||||
|         if (positionX - r < worldAabb.minX) { |  | ||||||
|             positionX = (worldAabb.minX | 0) + r; |  | ||||||
|             collidedThisSubtick = true; |  | ||||||
|         } |  | ||||||
|         if (positionY - r < worldAabb.minY) { |  | ||||||
|             positionY = (worldAabb.minY | 0) + r; |  | ||||||
|             collidedThisSubtick = true; |  | ||||||
|         } |  | ||||||
|         if (positionX + r > worldAabb.maxX) { |  | ||||||
|             positionX = (worldAabb.maxX | 0) - r; |  | ||||||
|             collidedThisSubtick = true; |  | ||||||
|         } |  | ||||||
|         if (positionY + r > worldAabb.maxY) { |  | ||||||
|             positionY = (worldAabb.maxY | 0) - r; |  | ||||||
|             collidedThisSubtick = true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (collidedThisSubtick) { |  | ||||||
|             hadAnyCollision = true; |  | ||||||
|         } else { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Zpomalení v důsledku kolize
 |  | ||||||
|     if (hadAnyCollision) { |  | ||||||
|         velocityX = ((velocityX * (collisionSlowdownFraction[0] | 0)) / (collisionSlowdownFraction[1] | 0)) | 0; |  | ||||||
|         velocityY = ((velocityY * (collisionSlowdownFraction[0] | 0)) / (collisionSlowdownFraction[1] | 0)) | 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Kolize s cíli
 |  | ||||||
|     let goalsIntersected: CircleId[] = []; |  | ||||||
| 
 |  | ||||||
|     const goalCandidates = gridGoals.queryCircle({ |  | ||||||
|         x: positionX, |  | ||||||
|         y: positionY, |  | ||||||
|     }, r); |  | ||||||
| 
 |  | ||||||
|     for (let i = 0; i < goalCandidates.length; i++) { |  | ||||||
|         const c = goalCandidates[i]; |  | ||||||
|         const dx = (positionX - c.x) | 0; // od středu cíle ke středu lodi
 |  | ||||||
|         const dy = (positionY - c.y) | 0; |  | ||||||
|         const distSq = dx * dx + dy * dy; |  | ||||||
|         const dist = Math.sqrt(distSq) | 0; |  | ||||||
|         const threshold = (r + c.radius) | 0; |  | ||||||
|         if (dist > threshold) { |  | ||||||
|             // Kolize nenastala
 |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Kolidujeme
 |  | ||||||
|         goalsIntersected.push(c); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return { |  | ||||||
|         shipPositionStart: { |  | ||||||
|             x: shipPositionStart.x, |  | ||||||
|             y: shipPositionStart.y, |  | ||||||
|         }, |  | ||||||
|         shipPositionEnd: { |  | ||||||
|             x: positionX, |  | ||||||
|             y: positionY, |  | ||||||
|         }, |  | ||||||
|         shipVelocityStart: { |  | ||||||
|             x: shipVelocityStart.x, |  | ||||||
|             y: shipVelocityStart.y, |  | ||||||
|         }, |  | ||||||
|         shipVelocityEnd: { |  | ||||||
|             x: velocityX, |  | ||||||
|             y: velocityY, |  | ||||||
|         }, |  | ||||||
|         goalsIntersected, |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Throws if any instruction is invalid (too large acceleration). |  | ||||||
|  */ |  | ||||||
| export function checkInstructions(instructions: Point[]): void { |  | ||||||
|     for (const i of instructions) { |  | ||||||
|         const x = i.x | 0; |  | ||||||
|         const y = i.y | 0; |  | ||||||
|         const lenSq = x * x + y * y; |  | ||||||
|         if (lenSq > 127 * 127) { |  | ||||||
|             throw new Error(`Instrukce x=${i.x} y=${i.y} přesáhla povolené zrychlení! (${Math.sqrt(lenSq)} > 127)`); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export async function simulate(world: World, instructions: Point[], periodicYield?: boolean, onYield?: (processedInstructions: number) => void): Promise<SimulationResult> { |  | ||||||
|     const gridAsteroids = new Grid(world.asteroids); |  | ||||||
|     const gridGoals = new Grid(world.goals); |  | ||||||
| 
 |  | ||||||
|     const ticks: TickResult[] = []; |  | ||||||
|     let latestPosition: Point = { |  | ||||||
|         x: world.shipStartX, |  | ||||||
|         y: world.shipStartY, |  | ||||||
|     }; |  | ||||||
|     let latestVelocity: Point = { |  | ||||||
|         x: 0, |  | ||||||
|         y: 0, |  | ||||||
|     }; |  | ||||||
|     let tickCount = 0; |  | ||||||
|     let goalsReached = 0; |  | ||||||
|     let reachedGoalIds: Set<number> = new Set(); |  | ||||||
|     let allGoalsReachedTick: number = undefined; |  | ||||||
| 
 |  | ||||||
|     for (let index = 0; index < instructions.length; index++) { |  | ||||||
|         const instruction = instructions[index]; |  | ||||||
|         const result = tick(gridAsteroids, gridGoals, world, world.shipRadius, latestPosition, latestVelocity, instruction); |  | ||||||
|         latestPosition.x = result.shipPositionEnd.x; |  | ||||||
|         latestPosition.y = result.shipPositionEnd.y; |  | ||||||
|         latestVelocity.x = result.shipVelocityEnd.x; |  | ||||||
|         latestVelocity.y = result.shipVelocityEnd.y; |  | ||||||
|         tickCount++; |  | ||||||
|         ticks.push(result); |  | ||||||
|         for (const goal of result.goalsIntersected) { |  | ||||||
|             if (!reachedGoalIds.has(goal.id)) { |  | ||||||
|                 reachedGoalIds.add(goal.id) |  | ||||||
|                 goalsReached++; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if (!allGoalsReachedTick && goalsReached >= world.goals.length) { |  | ||||||
|             allGoalsReachedTick = index; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (periodicYield && index > 0 && index % 5000 === 0) { |  | ||||||
|             if (onYield) { |  | ||||||
|                 onYield(index); |  | ||||||
|             } |  | ||||||
|             await timeout(0); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const messages: string[] = []; |  | ||||||
| 
 |  | ||||||
|     if (!allGoalsReachedTick && world.goals.length > 0) { |  | ||||||
|         messages.push("Nebyly dosaženy všechny cíle!"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (allGoalsReachedTick && allGoalsReachedTick < instructions.length - 1) { |  | ||||||
|         messages.push("Vaše instrukce pokračují i po dosažení posledního cíle! Instrukce po dosažení posledního cíle můžete bezpečně smazat a tím si vylepšit čas."); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return { |  | ||||||
|         goalsReached: reachedGoalIds, |  | ||||||
|         ticksExecuted: tickCount, |  | ||||||
|         tickResults: ticks, |  | ||||||
|         messages |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function generateLog(world: World, result: SimulationResult): string { |  | ||||||
|     const chunks: string[] = []; |  | ||||||
| 
 |  | ||||||
|     let goalStates = []; |  | ||||||
| 
 |  | ||||||
|     for (let i = 0; i < world.goals.length; i++) { |  | ||||||
|         goalStates.push(0); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     for (const tick of result.tickResults) { |  | ||||||
|         for (const goal of tick.goalsIntersected) { |  | ||||||
|             goalStates[goal.id] = 1; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         chunks.push(tick.shipPositionEnd.x.toString()); |  | ||||||
|         chunks.push(" "); |  | ||||||
|         chunks.push(tick.shipPositionEnd.y.toString()); |  | ||||||
|         chunks.push(" "); |  | ||||||
|         chunks.push(tick.shipVelocityEnd.x.toString()); |  | ||||||
|         chunks.push(" "); |  | ||||||
|         chunks.push(tick.shipVelocityEnd.y.toString()); |  | ||||||
|         chunks.push(" "); |  | ||||||
|         for (let i = 0; i < goalStates.length; i++) { |  | ||||||
|             chunks.push(goalStates[i].toString()); |  | ||||||
|         } |  | ||||||
|         chunks.push("\n"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return chunks.join(""); |  | ||||||
| } |  | ||||||
|  | @ -1,45 +0,0 @@ | ||||||
| export type Aabb = { |  | ||||||
|     minX: number; |  | ||||||
|     minY: number; |  | ||||||
|     maxX: number; |  | ||||||
|     maxY: number; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export type Circle = { |  | ||||||
|     x: number; |  | ||||||
|     y: number; |  | ||||||
|     radius: number; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export type CircleId = Circle &{ |  | ||||||
|     id: number; |  | ||||||
|     type: 'asteroid' | 'goal'; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export type World = Aabb & { |  | ||||||
|     shipStartX: number; |  | ||||||
|     shipStartY: number; |  | ||||||
|     shipRadius: number; |  | ||||||
|     asteroids: CircleId[]; |  | ||||||
|     goals: CircleId[]; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export type Point = { |  | ||||||
|     x: number; |  | ||||||
|     y: number; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export type TickResult = { |  | ||||||
|     shipPositionStart: Point; |  | ||||||
|     shipVelocityStart: Point; |  | ||||||
|     shipPositionEnd: Point; |  | ||||||
|     shipVelocityEnd: Point; |  | ||||||
|     goalsIntersected: CircleId[]; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export type SimulationResult = { |  | ||||||
|     ticksExecuted: number; |  | ||||||
|     goalsReached: Set<number>; |  | ||||||
|     tickResults: TickResult[]; |  | ||||||
|     messages: string[]; |  | ||||||
| }; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| export function timeout(ms: number): Promise<void> { |  | ||||||
|     return new Promise(resolve => setTimeout(resolve, ms)); |  | ||||||
| } |  | ||||||
|  | @ -1,16 +0,0 @@ | ||||||
| { |  | ||||||
| 	"compilerOptions": { |  | ||||||
| 		"noImplicitAny": true, |  | ||||||
| 		"lib": [ "dom", "esnext" ], |  | ||||||
| 		"module": "esnext", |  | ||||||
| 		"target": "ES2017", |  | ||||||
|         "strict": false, |  | ||||||
|         "moduleDetection": "force", |  | ||||||
| 		"moduleResolution": "node", |  | ||||||
| 	}, |  | ||||||
| 	"exclude": [ |  | ||||||
| 		"node_modules", |  | ||||||
| 		"static" |  | ||||||
| 	], |  | ||||||
| 	"include": ["src/**/*.ts"] |  | ||||||
| } |  | ||||||
|  | @ -121,7 +121,7 @@ Iterujeme přes všechny cíle `goal` a označíme je jako dosažené, pokud `eu | ||||||
| Všimněte si, že simulace má několik neintuitivních zjednodušení a vlastností: | Všimněte si, že simulace má několik neintuitivních zjednodušení a vlastností: | ||||||
| 
 | 
 | ||||||
| - Při úvodním posunu lodi o její rychlost **nekontrolujeme** kolize. | - Při úvodním posunu lodi o její rychlost **nekontrolujeme** kolize. | ||||||
| - Kolize kontrolujeme až jako průsečík finální pozice lodi s asteroidy či cíly. | - Kolize kontrolujeme až jako průsečík finální pozice lodi s asteroidy či cíle. | ||||||
| - Při kolizi s asteroidem se loď **neodrazí**, tedy nezmění se směr její rychlosti. | - Při kolizi s asteroidem se loď **neodrazí**, tedy nezmění se směr její rychlosti. | ||||||
| - Při řešení kolizí loď pouze posouváme ven z asteroidů. | - Při řešení kolizí loď pouze posouváme ven z asteroidů. | ||||||
| - Při kolizi nezáleží na tom, v jaké fázi řešení podkroků vydělíme rychlost lodi dvěma. | - Při kolizi nezáleží na tom, v jaké fázi řešení podkroků vydělíme rychlost lodi dvěma. | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue