-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bf9d23c
commit c388542
Showing
9 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { CoreMemory } from "./instruction" | ||
import { Dat } from "./instructions" | ||
|
||
export class Core { | ||
public readonly core: CoreMemory[] | ||
|
||
public constructor( | ||
public readonly coreSize = 80, | ||
) { | ||
this.core = new Array(coreSize).fill(Dat) | ||
} | ||
|
||
public write(memory: CoreMemory[], atIndex: number): void { | ||
memory.forEach((memoryValue, index) => { | ||
this.core[(atIndex + index) % this.coreSize] = memoryValue | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import * as Corewar from "./index" | ||
|
||
export { Corewar } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { Core } from "./core" | ||
import { interpret } from "./interpreter" | ||
import { Warrior } from "./warrior" | ||
import { WarriorCode } from "./warrior_code" | ||
|
||
|
||
export class Engine { | ||
public get tick(): number { | ||
return this._tick | ||
} | ||
|
||
private _tick = 0 | ||
|
||
public constructor( | ||
public readonly core: Core, | ||
private readonly warriors: Warrior[], | ||
) { | ||
} | ||
|
||
public runTick(): void { | ||
this.warriors.forEach(warrior => { | ||
const pointerWrapper = warrior.getNextPointer() | ||
if (pointerWrapper == null) { | ||
return | ||
} | ||
|
||
const result = interpret(this.core, pointerWrapper.pointer) | ||
pointerWrapper.tickFinished(result) | ||
}) | ||
|
||
this._tick += 1 | ||
} | ||
} | ||
|
||
|
||
export class EngineSetup { | ||
private readonly core: Core | ||
private readonly warriorCode = new Map<string, WarriorCode>() | ||
|
||
public constructor( | ||
coreSize = 80, | ||
) { | ||
this.core = new Core(coreSize) | ||
} | ||
|
||
public addWarriorCode(warriorCode: WarriorCode): void { | ||
this.warriorCode.set(warriorCode.name, warriorCode) | ||
} | ||
|
||
public init(): Engine | string { | ||
const initialCodeMemoryLength = Array.from(this.warriorCode.values()).reduce((result, current) => result + current.code.length, 0) | ||
const bufferSize = this.core.coreSize - initialCodeMemoryLength | ||
if (bufferSize < 0) { | ||
return `Coresize too small (initial warriors requires ${initialCodeMemoryLength} but ${this.core.coreSize} coresize given)` | ||
} | ||
|
||
const warriors: Warrior[] = [] | ||
const addWarrior = (instructionPointer: number, warriorCode: WarriorCode): void => { | ||
warriors.push(new Warrior(warriorCode, instructionPointer)) | ||
} | ||
|
||
let instructionPointer = 0 | ||
const bufferForEachWarriors = Math.floor(bufferSize / this.warriorCode.size) | ||
this.warriorCode.forEach(warriorCode => { | ||
addWarrior(instructionPointer, warriorCode) | ||
instructionPointer += warriorCode.code.length + bufferForEachWarriors | ||
}) | ||
|
||
return new Engine(this.core, warriors) | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./engine" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
const opecodes = [ | ||
"add", | ||
"mov", | ||
"jmp", | ||
"jmz", | ||
"dat", | ||
] as const | ||
export type RawOpecode = typeof opecodes[number] | ||
|
||
|
||
const modifiers = [ | ||
".a", | ||
".b", | ||
".ab", | ||
".ba", | ||
] as const | ||
export type Modifier = typeof modifiers[number] | ||
|
||
export type OperandType = ".a" | ".b" | ||
|
||
|
||
export type Opecode = { | ||
readonly opecode: RawOpecode | ||
readonly modifier: Modifier | ||
} | ||
|
||
|
||
const addressingModes = [ | ||
"#", // Immediate | ||
// "$", // Direct | ||
// "*", // A Indirect | ||
// "@", // B Indirect | ||
// "{", // A Pre-decrement Indirect | ||
// "}", // A Post-increment Indirect | ||
// "<", // B Pre-decrement Indirect | ||
// ">", // B Post-increment Indirect | ||
] as const | ||
export type AddressingMode = typeof addressingModes[number] | ||
|
||
export type Operand = { | ||
operand: number | ||
readonly addressingMode: AddressingMode | ||
} | ||
|
||
|
||
export type Instruction = { | ||
readonly opecode: Opecode | ||
|
||
readonly operandA: Operand | ||
readonly operandB: Operand | ||
} | ||
export type Data = number | ||
|
||
export type CoreMemory = Instruction | Data | ||
|
||
export const cloneMemory = (memory: CoreMemory): CoreMemory => { | ||
if (isInstruction(memory)) { | ||
return {...memory} | ||
} | ||
return memory | ||
} | ||
|
||
export const isInstruction = (memory: CoreMemory): memory is Instruction => !(typeof memory === "string") | ||
|
||
export const isZeroValue = (memory: CoreMemory): boolean => { | ||
if (isInstruction(memory)) { | ||
return memory.operandA.operand === 0 && memory.operandB.operand === 0 | ||
} | ||
return memory === 0 | ||
} | ||
|
||
export const compute = (memory: CoreMemory, computation: (value: number) => number, operandType: OperandType): CoreMemory => { | ||
if (!isInstruction(memory)) { | ||
return computation(memory) | ||
} | ||
|
||
switch (operandType) { | ||
case ".a": | ||
memory.operandA.operand = computation(memory.operandA.operand) | ||
return memory | ||
case ".b": | ||
memory.operandB.operand = computation(memory.operandB.operand) | ||
return memory | ||
} | ||
} | ||
|
||
|
||
export type ExecutionResult = { | ||
readonly result: "succeeded" | ||
readonly newPointer: number | ||
} | { | ||
readonly result: "failed" | ||
readonly failureReason: "dat" | "raw data" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Instruction } from "./instruction" | ||
|
||
export const Dat: Instruction = { | ||
opecode: { | ||
opecode: "dat", | ||
modifier: ".a", | ||
}, | ||
operandA: { | ||
addressingMode: "#", | ||
operand: 0, | ||
}, | ||
operandB: { | ||
addressingMode: "#", | ||
operand: 0, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { Core } from "./core" | ||
import { cloneMemory, compute, ExecutionResult, isInstruction, isZeroValue } from "./instruction" | ||
|
||
export const interpret = (core: Core, pointer: number): ExecutionResult => { | ||
const instruction = core.core[pointer] | ||
if (!isInstruction(instruction)) { | ||
return { | ||
result: "failed", | ||
failureReason: "raw data", | ||
} | ||
} | ||
|
||
const p = (relativePointer: number): number => (pointer + relativePointer) % core.coreSize | ||
|
||
switch (instruction.opecode.opecode) { | ||
case "add": | ||
// .ab実装 | ||
core.core[p(instruction.operandA.operand)] = compute(core.core[p(instruction.operandA.operand)], value => value + instruction.operandB.operand, ".b") | ||
return { | ||
result: "succeeded", | ||
newPointer: p(+1), | ||
} | ||
|
||
case "mov": | ||
core.core[p(instruction.operandB.operand)] = cloneMemory(core.core[p(instruction.operandA.operand)]) | ||
return { | ||
result: "succeeded", | ||
newPointer: p(+1), | ||
} | ||
|
||
case "jmp": | ||
return { | ||
result: "succeeded", | ||
newPointer: p(instruction.operandA.operand), | ||
} | ||
|
||
case "jmz": | ||
if (isZeroValue(core.core[p(instruction.operandB.operand)])) { | ||
return { | ||
result: "succeeded", | ||
newPointer: p(instruction.operandA.operand), | ||
} | ||
} | ||
return { | ||
result: "succeeded", | ||
newPointer: p(+1), | ||
} | ||
|
||
case "dat": | ||
return { | ||
result: "failed", | ||
failureReason: "dat", | ||
} | ||
} | ||
} | ||
|
||
/* | ||
// Bomber | ||
start add.ab #4, bmb | ||
mov.i bmb, @bmb | ||
jmp start | ||
bmb dat #0, #0 | ||
// Scanner | ||
scn add #10, ptr | ||
ptr jmz.f scn, 5 | ||
mov.i 2, >ptr | ||
jmp -1 | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { ExecutionResult } from "./instruction" | ||
import { WarriorCode } from "./warrior_code" | ||
|
||
|
||
type PointerWrapper = { | ||
readonly pointer: number | ||
readonly tickFinished: (result: ExecutionResult) => void | ||
} | ||
|
||
|
||
class WarriorProcess { | ||
public constructor( | ||
public instructionPointer: number, | ||
) { | ||
} | ||
} | ||
|
||
|
||
export class Warrior { | ||
public get name(): string { | ||
return this.code.name | ||
} | ||
private processPointer = 0 | ||
private aliveProcesses: WarriorProcess[] = [] | ||
private deadProcesses: WarriorProcess[] = [] | ||
|
||
public constructor( | ||
public readonly code: WarriorCode, | ||
instructionPointer: number, | ||
) { | ||
this.aliveProcesses.push(new WarriorProcess(instructionPointer)) | ||
} | ||
|
||
public getNextPointer(): PointerWrapper | null { | ||
const process = this.getNextAliveProcess() | ||
if (process == null) { | ||
return null | ||
} | ||
|
||
return { | ||
pointer: process.instructionPointer, | ||
tickFinished: result => { | ||
switch (result.result) { | ||
case "succeeded": | ||
process.instructionPointer = result.newPointer | ||
this.processPointer = (this.processPointer + 1) % this.aliveProcesses.length | ||
break | ||
case "failed": | ||
this.aliveProcesses.splice(this.processPointer, 1) | ||
this.deadProcesses.push(process) | ||
if (this.aliveProcesses.length > 0) { | ||
this.processPointer = this.processPointer % this.aliveProcesses.length | ||
} | ||
break | ||
default: { | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const _: never = result | ||
break | ||
} | ||
} | ||
}, | ||
} | ||
} | ||
|
||
/** @throws */ | ||
private getNextAliveProcess(): WarriorProcess | null { | ||
return this.aliveProcesses[this.processPointer] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { Instruction } from "./instruction" | ||
|
||
export type WarriorCode = { | ||
readonly name: string | ||
readonly code: Instruction[] | ||
} |