Compare commits
	
		
			2 commits
		
	
	
		
			cd93ccb063
			...
			fc2c8ac1aa
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fc2c8ac1aa | |||
| 4fc86cf354 | 
					 12 changed files with 774 additions and 1 deletions
				
			
		
							
								
								
									
										1
									
								
								asteracer-typescript/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								asteracer-typescript/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| node_modules | ||||
							
								
								
									
										5
									
								
								asteracer-typescript/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								asteracer-typescript/README.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| 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]` | ||||
							
								
								
									
										34
									
								
								asteracer-typescript/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								asteracer-typescript/index.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| 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
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								asteracer-typescript/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,236 @@ | |||
| { | ||||
|   "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" | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										17
									
								
								asteracer-typescript/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								asteracer-typescript/package.json
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| { | ||||
|   "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" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										72
									
								
								asteracer-typescript/src/asteracer/grid.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								asteracer-typescript/src/asteracer/grid.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,72 @@ | |||
| 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; | ||||
| } | ||||
							
								
								
									
										103
									
								
								asteracer-typescript/src/asteracer/parse_map.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								asteracer-typescript/src/asteracer/parse_map.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,103 @@ | |||
| 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]; | ||||
| } | ||||
							
								
								
									
										241
									
								
								asteracer-typescript/src/asteracer/simulation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								asteracer-typescript/src/asteracer/simulation.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,241 @@ | |||
| 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(""); | ||||
| } | ||||
							
								
								
									
										45
									
								
								asteracer-typescript/src/asteracer/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								asteracer-typescript/src/asteracer/types.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| 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[]; | ||||
| }; | ||||
							
								
								
									
										3
									
								
								asteracer-typescript/src/util.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								asteracer-typescript/src/util.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| export function timeout(ms: number): Promise<void> { | ||||
|     return new Promise(resolve => setTimeout(resolve, ms)); | ||||
| } | ||||
							
								
								
									
										16
									
								
								asteracer-typescript/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								asteracer-typescript/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| { | ||||
| 	"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í: | ||||
| 
 | ||||
| - 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íle. | ||||
| - Kolize kontrolujeme až jako průsečík finální pozice lodi s asteroidy či cíly. | ||||
| - 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 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