Ready-to-run game starters. Each script is a playable game you can extend with new rooms, questions, or units without touching the core engine.
A three-room dungeon crawler with branching navigation, a locked door, and an inventory system. Demonstrates how a Class bundles player state, how Dictionary stores room data keyed by location string, and how List:Contains gates progress.
Modules: Konsol List Dictionary Class
Usage:
minks text_adventure.ks
Sample session:
DUNGEON ESCAPE
Find the gold coin hidden in the vault. Type 'help' for commands.
=== Cave Entrance ===
Cold air and damp stone walls surround you. A passage leads east.
On the floor: torch.
> take
You pick up the torch.
> go east
=== Winding Corridor ===
A narrow passage runs east and west. The eastern door is heavy iron.
On the floor: iron key.
> take
You pick up the iron key.
> go east
You use the iron key. The heavy door grinds open.
=== Ancient Vault ===
Dust-covered carvings line the walls. A stone altar stands in the centre.
On the floor: gold coin.
> take
You pick up the gold coin.
The gold catches the torchlight. You have won!
Script:
// text_adventure.ks - Branching text adventure with inventory and locked doors
// Usage: minks text_adventure.ks
//
// Three rooms laid out west → east:
// Cave Entrance · Winding Corridor · Ancient Vault (locked until you find the key)
//
// Collect the iron key in the corridor to unlock the vault.
// Pick up the gold coin inside to win.
//
// Commands: look · go east · go west · take · inventory · quit
//
// Modules: Konsol, List, Dictionary, Class
// ── Player class ──────────────────────────────────────────────────────────────
// Bundling all mutable player state into a class makes it easy to extend later
// (e.g. add a "stamina" or "damage" field without touching game logic).
Class:Create(Player) {
Var:String location; // current room: "entrance", "corridor", or "vault"
Var:Number hp;
function isAlive() : Boolean {
return hp > 0;
}
}
// ── Room data stored in Dictionaries ──────────────────────────────────────────
// One Dictionary per property. The player's location string is the key for all three,
// so a single Dictionary:Get call retrieves the right data for wherever the player is.
Dictionary:New roomNames;
Dictionary:Set("entrance", "Cave Entrance", roomNames);
Dictionary:Set("corridor", "Winding Corridor", roomNames);
Dictionary:Set("vault", "Ancient Vault", roomNames);
Dictionary:New roomDescs;
Dictionary:Set("entrance", "Cold air and damp stone walls surround you. A passage leads east.", roomDescs);
Dictionary:Set("corridor", "A narrow passage runs east and west. The eastern door is heavy iron.", roomDescs);
Dictionary:Set("vault", "Dust-covered carvings line the walls. A stone altar stands in the centre.", roomDescs);
// Floor items are removed from this Dictionary once the player picks them up.
// An empty string means the floor is clear.
Dictionary:New floorItems;
Dictionary:Set("entrance", "torch", floorItems);
Dictionary:Set("corridor", "iron key", floorItems);
Dictionary:Set("vault", "gold coin", floorItems);
// ── Player and inventory ───────────────────────────────────────────────────────
Class:Player player;
player.location = "entrance";
player.hp = 100;
List:New inventory:String;
// ── Room display helper ────────────────────────────────────────────────────────
// Receives all data as parameters so it does not need to touch global Dictionaries.
function showRoom(String rName, String rDesc, String rItem) {
Konsol:Print("");
Konsol:Print("=== " + rName + " ===");
Konsol:Print(rDesc);
if (rItem != "") {
Konsol:Print("On the floor: " + rItem + ".");
}
}
// ── Pre-declared working variables ────────────────────────────────────────────
Var:String input;
Var:String cmd;
Var:String rName;
Var:String rDesc;
Var:String rItem;
Var:String loc;
Var:String invItem;
Var:Boolean running = true;
Var:Boolean hasKey;
Var:Number invSize;
// ── Opening screen ────────────────────────────────────────────────────────────
Konsol:Print("DUNGEON ESCAPE");
Konsol:Print("Find the gold coin hidden in the vault. Type 'help' for commands.");
Dictionary:Get(player.location, roomNames, rName);
Dictionary:Get(player.location, roomDescs, rDesc);
Dictionary:Get(player.location, floorItems, rItem);
showRoom(rName, rDesc, rItem);
// ── Main game loop ────────────────────────────────────────────────────────────
while (running) {
Konsol:Input("> ", input);
String:Trim(input, cmd);
String:Lower(cmd, cmd);
if (cmd == "quit" || cmd == "q") {
Konsol:Print("Farewell, adventurer.");
running = false;
} else if (cmd == "help") {
Konsol:Print("Commands: look · go east · go west · take · inventory · quit");
} else if (cmd == "look" || cmd == "l") {
Dictionary:Get(player.location, roomNames, rName);
Dictionary:Get(player.location, roomDescs, rDesc);
Dictionary:Get(player.location, floorItems, rItem);
showRoom(rName, rDesc, rItem);
} else if (cmd == "inventory" || cmd == "inv" || cmd == "i") {
List:Size(inventory, invSize);
if (invSize == 0) {
Konsol:Print("You are carrying nothing.");
} else {
Konsol:Print("Carrying:");
for (Number k = 0; k < invSize; k++) {
List:Get(k, inventory, invItem);
Konsol:Print(" - " + invItem);
}
}
} else if (cmd == "take" || cmd == "get" || cmd == "pick up") {
// Read what is on the floor, then clear it from the Dictionary.
Dictionary:Get(player.location, floorItems, rItem);
if (rItem == "") {
Konsol:Print("There is nothing here to take.");
} else {
List:Push(rItem, inventory);
Dictionary:Set(player.location, "", floorItems);
Konsol:Print("You pick up the " + rItem + ".");
if (rItem == "gold coin") {
Konsol:Print("");
Konsol:Print("The gold catches the torchlight. You have won!");
running = false;
}
}
} else if (cmd == "go east" || cmd == "east" || cmd == "e") {
loc = player.location;
if (loc == "entrance") {
player.location = "corridor";
Dictionary:Get(player.location, roomNames, rName);
Dictionary:Get(player.location, roomDescs, rDesc);
Dictionary:Get(player.location, floorItems, rItem);
showRoom(rName, rDesc, rItem);
} else if (loc == "corridor") {
// The vault door is locked - the iron key is required.
List:Contains("iron key", inventory, hasKey);
if (hasKey) {
player.location = "vault";
Konsol:Print("You use the iron key. The heavy door grinds open.");
Dictionary:Get(player.location, roomNames, rName);
Dictionary:Get(player.location, roomDescs, rDesc);
Dictionary:Get(player.location, floorItems, rItem);
showRoom(rName, rDesc, rItem);
} else {
Konsol:Print("The iron door is locked. You need a key.");
}
} else {
Konsol:Print("There is no passage to the east.");
}
} else if (cmd == "go west" || cmd == "west" || cmd == "w") {
loc = player.location;
if (loc == "corridor") {
player.location = "entrance";
Dictionary:Get(player.location, roomNames, rName);
Dictionary:Get(player.location, roomDescs, rDesc);
Dictionary:Get(player.location, floorItems, rItem);
showRoom(rName, rDesc, rItem);
} else if (loc == "vault") {
player.location = "corridor";
Dictionary:Get(player.location, roomNames, rName);
Dictionary:Get(player.location, roomDescs, rDesc);
Dictionary:Get(player.location, floorItems, rItem);
showRoom(rName, rDesc, rItem);
} else {
Konsol:Print("There is no passage to the west.");
}
} else {
Konsol:Print("Unknown command. Type 'help' for a list.");
}
}
Five general-knowledge questions presented in a random order every run. Demonstrates parallel List collections, a Fisher-Yates shuffle over an Array index, and Math:Random + Math:Floor for integer random numbers.
Modules: Math Konsol List Array
Usage:
minks trivia_quiz.ks
Sample session:
TRIVIA QUIZ
Five questions. Enter 1, 2, or 3 for your answer.
Question 1 of 5: Which planet is closest to the Sun?
1) Earth
2) Venus
3) Mercury
Answer: 3
Correct!
Question 2 of 5: How many sides does a hexagon have?
1) 4
2) 6
3) 8
Answer: 1
Wrong! The answer was: 6
...
You scored 4 out of 5.
Good effort!
Script:
// trivia_quiz.ks - Interactive trivia quiz with shuffled questions and score tracking
// Usage: minks trivia_quiz.ks
//
// Five questions, each with three choices. Enter 1, 2, or 3 to answer.
// Questions are presented in a random order each run via a Fisher-Yates shuffle.
//
// Modules: Math, Konsol, List, Array
// ── Questions and answers ─────────────────────────────────────────────────────
// Five parallel Lists: one for question text, one per answer option, one for the
// correct choice (1 = A, 2 = B, 3 = C). Index i across all lists = question i.
List:New questions:String;
List:Push("What is the capital of France?", questions);
List:Push("How many sides does a hexagon have?", questions);
List:Push("Which planet is closest to the Sun?", questions);
List:Push("What gas do plants absorb during photosynthesis?", questions);
List:Push("Who wrote Romeo and Juliet?", questions);
List:New optA:String;
List:Push("London", optA);
List:Push("4", optA);
List:Push("Earth", optA);
List:Push("Oxygen", optA);
List:Push("Dickens", optA);
List:New optB:String;
List:Push("Berlin", optB);
List:Push("6", optB);
List:Push("Venus", optB);
List:Push("Carbon dioxide", optB);
List:Push("Shakespeare", optB);
List:New optC:String;
List:Push("Paris", optC);
List:Push("8", optC);
List:Push("Mercury", optC);
List:Push("Nitrogen", optC);
List:Push("Chaucer", optC);
// Correct answer: 1 = A, 2 = B, 3 = C
List:New answers:Number;
List:Push(3, answers); // Paris = C
List:Push(2, answers); // 6 = B
List:Push(3, answers); // Mercury = C
List:Push(2, answers); // CO₂ = B
List:Push(2, answers); // Shakespeare = B
// ── Shuffle the question order (Fisher-Yates) ─────────────────────────────────
// Build an index array [0, 1, 2, 3, 4] then shuffle it in-place.
// Using a separate index array means the five parallel lists stay untouched;
// we just look up order[q] at quiz time instead of q directly.
Array:New order[5]:Number;
for (Number k = 0; k < 5; k++) {
order[k] = k;
}
Var:Number swapI = 4;
Var:Number swapJ;
Var:Number swapTemp;
Var:Number rnd;
while (swapI > 0) {
// Pick a random index in [0, swapI] by taking a float in [0, swapI+1) and flooring it.
Math:Random(swapI + 1, rnd);
Math:Floor(rnd, swapJ);
// Swap order[swapI] ↔ order[swapJ]
swapTemp = order[swapI];
order[swapI] = order[swapJ];
order[swapJ] = swapTemp;
swapI = swapI - 1;
}
// ── Pre-declared working variables ────────────────────────────────────────────
Var:Number score = 0;
Var:Number qIdx;
Var:String qText;
Var:String a;
Var:String b;
Var:String c;
Var:Number correct;
Var:Number answer;
Var:Number qNum;
// ── Quiz loop ─────────────────────────────────────────────────────────────────
Konsol:Print("TRIVIA QUIZ");
Konsol:Print("Five questions. Enter 1, 2, or 3 for your answer.");
Konsol:Print("");
for (Number q = 0; q < 5; q++) {
// Use the shuffled index to look up question data.
qIdx = order[q];
List:Get(qIdx, questions, qText);
List:Get(qIdx, optA, a);
List:Get(qIdx, optB, b);
List:Get(qIdx, optC, c);
List:Get(qIdx, answers, correct);
qNum = q + 1;
Konsol:Print("Question ${qNum} of 5: ${qText}");
Konsol:Print(" 1) ${a}");
Konsol:Print(" 2) ${b}");
Konsol:Print(" 3) ${c}");
// Konsol:Input stores the value as a Number when the input looks like one.
Konsol:Input("Answer: ", answer);
if (answer == correct) {
Konsol:Print("Correct!");
score = score + 1;
} else {
// Tell the player what the right answer was.
if (correct == 1) { Konsol:Print("Wrong! The answer was: " + a); }
if (correct == 2) { Konsol:Print("Wrong! The answer was: " + b); }
if (correct == 3) { Konsol:Print("Wrong! The answer was: " + c); }
}
Konsol:Print("");
}
// ── Final score ───────────────────────────────────────────────────────────────
Konsol:Print("You scored ${score} out of 5.");
if (score == 5) {
Konsol:Print("Perfect score! Outstanding!");
} else if (score >= 3) {
Konsol:Print("Good effort!");
} else {
Konsol:Print("Better luck next time.");
}
A two-unit skirmish on a 5×5 tile grid. You command the Knight [K]; the Guard [G] uses a simple one-step AI that moves toward you and strikes automatically when adjacent. Demonstrates Class for units, a flat Array for the map grid, and Math:Random for combat variation.
Modules: Array List Dictionary Math Class
Usage:
minks strategy_game.ks
Sample session:
BATTLE ON THE GRID
[K] = you (Knight) [G] = enemy (Guard) [#] = wall
Move: n s e w Attack: a
Turn 1 | Knight HP: 30/30 Guard HP: 25/25
+---------------+
|[K] . . [#] . |
| . . . [#] . |
| . . . . . |
| . . . [#] . |
| . . . . [G]|
+---------------+
> e
> e
...
> a
You strike the Guard for 11 damage! (Guard HP: 14)
The Guard strikes you for 7 damage! (Your HP: 23)
Script:
// strategy_game.ks - Turn-based strategy on a 5×5 grid
// Usage: minks strategy_game.ks
//
// You are the Knight [K]. Defeat the Guard [G] before it defeats you.
//
// Each turn:
// n / s / e / w - move one tile
// a - attack (only works when adjacent to the Guard)
//
// The Guard moves one step toward you every turn and strikes automatically
// when adjacent. Combat deals base damage plus a small random bonus.
//
// Modules: Array, List, Map, Math, Class
// ── Unit class ────────────────────────────────────────────────────────────────
// All unit state lives here. Methods keep HP clamped at zero.
Class:Create(Unit) {
Var:String name;
Var:Number hp;
Var:Number maxHp;
Var:Number attack;
Var:Number x;
Var:Number y;
function isAlive() : Boolean {
return hp > 0;
}
function takeDamage(Number dmg) {
hp = hp - dmg;
if (hp < 0) { hp = 0; }
}
}
// ── Grid ──────────────────────────────────────────────────────────────────────
// 5×5 open ground. Cell (col, row) maps to flat index: row * 5 + col.
// Value 0 = open, 1 = wall. Extend this to add terrain features.
Array:New grid[25]:Number;
// Add a short wall in the middle column to force flanking manoeuvres.
grid[7] = 1; // (col 2, row 1)
grid[12] = 1; // (col 2, row 2)
grid[17] = 1; // (col 2, row 3)
// ── Units ─────────────────────────────────────────────────────────────────────
Class:Unit knight;
knight.name = "Knight";
knight.hp = 30;
knight.maxHp = 30;
knight.attack = 8;
knight.x = 0;
knight.y = 0;
Class:Unit guard;
guard.name = "Guard";
guard.hp = 25;
guard.maxHp = 25;
guard.attack = 6;
guard.x = 4;
guard.y = 4;
// ── Pre-declared working variables ────────────────────────────────────────────
Var:Boolean running = true;
Var:String input;
Var:String cmd;
Var:Number newX;
Var:Number newY;
Var:Number ex;
Var:Number ey;
Var:Number dx;
Var:Number dy;
Var:Number absDx;
Var:Number absDy;
Var:Number dist;
Var:Number dmg;
Var:Number rndBonus;
Var:Number tileIdx;
Var:String line;
Var:Number turn = 1;
Var:Number khp;
Var:Number ghp;
// ── Game loop ─────────────────────────────────────────────────────────────────
Konsol:Print("BATTLE ON THE GRID");
Konsol:Print("[K] = you (Knight) [G] = enemy (Guard) [#] = wall");
Konsol:Print("Move: n s e w Attack: a");
Konsol:Print("");
while (running) {
// ── Draw the 5×5 grid ─────────────────────────────────────────────────────
// Read HP into plain vars so they can be used inside ${}.
khp = knight.hp;
ghp = guard.hp;
Konsol:Print("Turn ${turn} | Knight HP: ${khp}/${knight.maxHp} Guard HP: ${ghp}/${guard.maxHp}");
Konsol:Print("+---------------+");
for (Number row = 0; row < 5; row++) {
line = "|";
for (Number col = 0; col < 5; col++) {
if (knight.x == col && knight.y == row) {
line = line + "[K]";
} else if (guard.x == col && guard.y == row) {
line = line + "[G]";
} else {
tileIdx = row * 5 + col;
if (grid[tileIdx] == 1) {
line = line + "[#]";
} else {
line = line + " . ";
}
}
}
line = line + "|";
Konsol:Print(line);
}
Konsol:Print("+---------------+");
// ── Player input ──────────────────────────────────────────────────────────
Konsol:Input("> ", input);
String:Trim(input, cmd);
String:Lower(cmd, cmd);
// ── Move ──────────────────────────────────────────────────────────────────
newX = knight.x;
newY = knight.y;
if (cmd == "n") { newY = newY - 1; }
else if (cmd == "s") { newY = newY + 1; }
else if (cmd == "e") { newX = newX + 1; }
else if (cmd == "w") { newX = newX - 1; }
if (cmd == "n" || cmd == "s" || cmd == "e" || cmd == "w") {
if (newX < 0 || newX > 4 || newY < 0 || newY > 4) {
Konsol:Print("You cannot move that way - edge of the grid.");
} else if (newX == guard.x && newY == guard.y) {
Konsol:Print("The Guard blocks that tile. Use 'a' to attack.");
} else {
tileIdx = newY * 5 + newX;
if (grid[tileIdx] == 1) {
Konsol:Print("A wall blocks the way.");
} else {
knight.x = newX;
knight.y = newY;
}
}
}
// ── Attack ────────────────────────────────────────────────────────────────
if (cmd == "a" || cmd == "attack") {
dx = knight.x - guard.x;
dy = knight.y - guard.y;
Math:Abs(dx, absDx);
Math:Abs(dy, absDy);
dist = absDx + absDy;
if (dist > 1) {
Konsol:Print("The Guard is too far away. Move adjacent first.");
} else {
// Add a random bonus (0–3) to keep each fight unpredictable.
Math:Random(4, rndBonus);
Math:Floor(rndBonus, rndBonus);
dmg = knight.attack + rndBonus;
guard.takeDamage(dmg);
ghp = guard.hp;
Konsol:Print("You strike the Guard for ${dmg} damage! (Guard HP: ${ghp})");
}
}
// ── Check win ─────────────────────────────────────────────────────────────
if (guard.isAlive() == false) {
Konsol:Print("");
Konsol:Print("The Guard collapses. Victory!");
running = false;
}
// ── Enemy turn ────────────────────────────────────────────────────────────
if (running) {
// Move one step toward the Knight (horizontal first, then vertical).
dx = knight.x - guard.x;
dy = knight.y - guard.y;
ex = guard.x;
ey = guard.y;
if (dx > 0) { ex = ex + 1; }
else if (dx < 0) { ex = ex - 1; }
else if (dy > 0) { ey = ey + 1; }
else if (dy < 0) { ey = ey - 1; }
// Validate: in bounds, not a wall, not the Knight's tile.
if (ex >= 0 && ex <= 4 && ey >= 0 && ey <= 4) {
tileIdx = ey * 5 + ex;
if (grid[tileIdx] != 1) {
if (ex != knight.x || ey != knight.y) {
guard.x = ex;
guard.y = ey;
}
}
}
// Attack if adjacent after moving.
dx = knight.x - guard.x;
dy = knight.y - guard.y;
Math:Abs(dx, absDx);
Math:Abs(dy, absDy);
dist = absDx + absDy;
if (dist <= 1) {
Math:Random(3, rndBonus);
Math:Floor(rndBonus, rndBonus);
dmg = guard.attack + rndBonus;
knight.takeDamage(dmg);
khp = knight.hp;
Konsol:Print("The Guard strikes you for ${dmg} damage! (Your HP: ${khp})");
}
// Check lose.
if (knight.isAlive() == false) {
Konsol:Print("");
Konsol:Print("You have fallen in battle. Game over.");
running = false;
}
turn = turn + 1;
}
}