443 lines
11 KiB
TypeScript
443 lines
11 KiB
TypeScript
/*
|
|
* Verrückte Turing-Maschinen — A turing complete game
|
|
* Copyright (C) 2016-2018 kcf <vidofnir@folksprak.org>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
module mod_vtm
|
|
{
|
|
|
|
export module mod_model
|
|
{
|
|
|
|
export module mod_game
|
|
{
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export type type_game =
|
|
{
|
|
world : mod_world.type_world;
|
|
token : lib_errormonade.type_errormonade<mod_token.type_token>;
|
|
task : mod_task.type_task;
|
|
testindex : lib_errormonade.type_errormonade<int>;
|
|
mode : mod_mode.type_mode;
|
|
listeners : {[event : string] : Array<(data ?: any)=>void>};
|
|
}
|
|
;
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function create
|
|
(
|
|
task : mod_task.type_task
|
|
)
|
|
: type_game
|
|
{
|
|
let game : type_game =
|
|
{
|
|
"world": undefined,
|
|
"token": undefined,
|
|
"task": task,
|
|
"testindex": undefined,
|
|
"mode": undefined,
|
|
"listeners": undefined,
|
|
}
|
|
;
|
|
game.listeners =
|
|
{
|
|
"change_task": [],
|
|
"change_world": [],
|
|
"change_token": [],
|
|
"change_mode": [],
|
|
}
|
|
;
|
|
world_clear(game, false);
|
|
reset(game, false);
|
|
return game;
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function listen
|
|
(
|
|
game : type_game,
|
|
event : string,
|
|
procedure : (data ?: any)=>void
|
|
)
|
|
: void
|
|
{
|
|
if (event in game.listeners)
|
|
{
|
|
game.listeners[event].push(procedure);
|
|
}
|
|
else
|
|
{
|
|
let message : string = "kein Ereignis mit diesem Name";
|
|
throw (new Error(message));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
function notify
|
|
(
|
|
game : type_game,
|
|
event : string,
|
|
data : any = {}
|
|
) : void
|
|
{
|
|
if (event in game.listeners)
|
|
{
|
|
game.listeners[event].forEach
|
|
(
|
|
procedure =>
|
|
{
|
|
procedure(data);
|
|
}
|
|
)
|
|
;
|
|
}
|
|
else
|
|
{
|
|
let message : string = "kein Ereignis mit diesem Name";
|
|
throw (new Error(message));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function reset
|
|
(
|
|
game : type_game,
|
|
inform : boolean = true
|
|
)
|
|
: void
|
|
{
|
|
game.token = (lib_errormonade.create_nothing<mod_token.type_token>());
|
|
game.testindex = (lib_errormonade.create_nothing<int>());
|
|
game.mode = mod_mode.initial;
|
|
if (inform)
|
|
{
|
|
notify(game, "change_token", {});
|
|
notify(game, "change_mode", {});
|
|
}
|
|
else
|
|
{
|
|
// nothing tun
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function task_read
|
|
(
|
|
game : type_game
|
|
)
|
|
: mod_task.type_task
|
|
{
|
|
return game.task;
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function task_set
|
|
(
|
|
game : type_game,
|
|
task : mod_task.type_task
|
|
) : void
|
|
{
|
|
game.task = task;
|
|
// game.world_clear();
|
|
notify(game, "change_task", {});
|
|
reset(game);
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function world_read
|
|
(
|
|
game : type_game
|
|
)
|
|
: mod_world.type_world
|
|
{
|
|
return game.world;
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function world_set
|
|
(
|
|
game : type_game,
|
|
world : mod_world.type_world,
|
|
inform : boolean = true
|
|
)
|
|
: void
|
|
{
|
|
game.world = world;
|
|
if (inform)
|
|
{
|
|
notify(game, "change_world", {});
|
|
}
|
|
else
|
|
{
|
|
// nothing tun
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function world_clear
|
|
(
|
|
game : type_game,
|
|
inform : boolean = true
|
|
)
|
|
: void
|
|
{
|
|
game.world = mod_world.blanko();
|
|
if (inform)
|
|
{
|
|
notify(game, "change_world", {});
|
|
}
|
|
else
|
|
{
|
|
// nothing tun
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function token_read
|
|
(
|
|
game : type_game
|
|
)
|
|
: lib_errormonade.type_errormonade<mod_token.type_token>
|
|
{
|
|
return game.token;
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function mode_read
|
|
(
|
|
game : type_game
|
|
)
|
|
: mod_mode.type_mode
|
|
{
|
|
return game.mode;
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function world_tile_change
|
|
(
|
|
game : type_game,
|
|
spot : mod_spot.type_spot,
|
|
inverted : boolean = false
|
|
)
|
|
: void
|
|
{
|
|
if (! (game.mode === mod_mode.initial))
|
|
{
|
|
let message : string = "gesperrt";
|
|
}
|
|
else
|
|
{
|
|
mod_world.tile_change(game.world, spot, inverted);
|
|
notify
|
|
(
|
|
game,
|
|
"change_world",
|
|
{
|
|
"kind": "tile_change",
|
|
"data":
|
|
{
|
|
"spot": spot,
|
|
"inverted": inverted,
|
|
"tile": mod_world.tile_get(game.world, spot),
|
|
}
|
|
}
|
|
)
|
|
;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function world_tile_rotate
|
|
(
|
|
game : type_game,
|
|
spot : mod_spot.type_spot,
|
|
increment : int = +1
|
|
)
|
|
: void
|
|
{
|
|
if (! (game.mode === mod_mode.initial))
|
|
{
|
|
let message : string = "gesperrt";
|
|
}
|
|
else
|
|
{
|
|
mod_world.tile_rotate(game.world, spot, increment);
|
|
notify(game, "change_world", {});
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @author kcf <vidofnir@folksprak.org>
|
|
*/
|
|
export function resume
|
|
(
|
|
game : type_game
|
|
)
|
|
: void
|
|
{
|
|
switch (game.mode)
|
|
{
|
|
case mod_mode.initial:
|
|
{
|
|
game.mode = mod_mode.uncertain;
|
|
game.testindex = (lib_errormonade.create_just<int>(0));
|
|
notify(game, "change_mode", {});
|
|
break;
|
|
}
|
|
case mod_mode.uncertain:
|
|
{
|
|
if (! lib_errormonade.filled<mod_token.type_token>(game.token))
|
|
{
|
|
let test : mod_test.type_test = mod_task.tests(game.task)[lib_errormonade.read<int>(game.testindex)];
|
|
let tape : Array<mod_symbol.type_symbol> = lib_list.copy<mod_symbol.type_symbol>(mod_test.input(test));
|
|
let spot : mod_spot.type_spot = mod_world.generator_finden(game.world);
|
|
game.token = (
|
|
lib_errormonade.create_just<mod_token.type_token>
|
|
(
|
|
mod_token.create
|
|
(
|
|
tape,
|
|
spot
|
|
)
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
let token : mod_token.type_token = lib_errormonade.read<mod_token.type_token>(game.token);
|
|
let spot : mod_spot.type_spot = mod_token.spot_read(token);
|
|
let actuator_ : lib_errormonade.type_errormonade<mod_actuator.type_actuator> = mod_world.tile_get(game.world, spot);
|
|
let actuator : mod_actuator.type_actuator = (
|
|
lib_errormonade.filled<mod_actuator.type_actuator>(actuator_)
|
|
? lib_errormonade.read<mod_actuator.type_actuator>(actuator_)
|
|
: mod_actuator.mod_rejector.create_extended()
|
|
);
|
|
mod_actuator.use(actuator, token);
|
|
let state : mod_state.type_state = mod_token.state_read(token);
|
|
if (state === mod_state.running)
|
|
{
|
|
// nothing tun
|
|
}
|
|
else if ((state === mod_state.accepted) || (state === mod_state.rejected))
|
|
{
|
|
let accepted : boolean = (state === mod_state.accepted);
|
|
let output : Array<mod_symbol.type_symbol> = mod_token.tape_read(token);
|
|
game.token = (lib_errormonade.create_nothing<mod_token.type_token>());
|
|
let testindex : int = lib_errormonade.read<int>(game.testindex);
|
|
let tests : Array<mod_test.type_test> = mod_task.tests(game.task);
|
|
let test : mod_test.type_test = tests[testindex];
|
|
if (! mod_test.pruefen(test, accepted, output))
|
|
{
|
|
game.mode = mod_mode.wrong;
|
|
notify(game, "change_mode", {});
|
|
}
|
|
else
|
|
{
|
|
testindex += 1;
|
|
if (testindex < tests.length)
|
|
{
|
|
// nächsten Test auswählen
|
|
game.testindex = (lib_errormonade.create_just<int>(testindex));
|
|
}
|
|
else
|
|
{
|
|
// auf Modus "correct" change
|
|
game.testindex = (lib_errormonade.create_nothing<int>());
|
|
game.mode = mod_mode.correct;
|
|
notify(game, "change_mode", {});
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
let message : string = "unbehandelter Zustand";
|
|
throw (new Error(message));
|
|
}
|
|
}
|
|
notify(game, "change_token", {});
|
|
break;
|
|
}
|
|
case mod_mode.wrong:
|
|
{
|
|
// nothing tun
|
|
break;
|
|
}
|
|
case mod_mode.correct:
|
|
{
|
|
// nothing tun
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
let message : string = "unbehandelter Modus";
|
|
throw (new Error(message));
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|