Basics of Oberon-07
Descripción del contenido de la página
Conversión de antiguos programas de BASIC a Oberon-07 para aprender los rudimentos de este lenguaje.
Etiquetas:
Chase
(*
Chase
Original version in BASIC:
Anonymous. Published in 1977 in The Best of Creative Computing
Volumen 2, page 253:
https://www.atariarchives.org/bcc2/showpage.php?page=253
This version in Oberon-07:
Copyright (c) 2022, 2023, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Last modified 20231215T1548+0100.
*)
MODULE chase;
IMPORT
Input0,
Out,
obqChar,
obqKey,
obqRandom,
obqScreen;
CONST
(* Map size: *)
xSize = 20;
lastX = xSize-1;
ySize = 10;
lastY = ySize-1;
(* Values of `theEnd` beside the default 0: *)
quit = 1;
electrified = 2;
killed = 3;
victory = 4;
machines = 5;
lastMachine = machines-1;
machinesDrag = 2; (* probability not moving: 0=0%, 1=50%, 2=66%, 3=75%, etc. *)
(* Map content: *)
empty = " ";
fence = "X";
machine = "m";
human = "@";
fences = 15; (* inner obstacles, not the border *)
VAR
map : ARRAY ySize,xSize OF CHAR;
machineX : ARRAY machines OF INTEGER;
machineY : ARRAY machines OF INTEGER;
operative : ARRAY machines OF BOOLEAN;
destroyedMachines : INTEGER; (* counter *)
humanX : INTEGER;
humanY : INTEGER;
theEnd : INTEGER;
moving : BOOLEAN;
xInc : INTEGER;
yInc : INTEGER;
PROCEDURE Instructions;
(*
Display the game instructions and wait for a key.
*)
BEGIN
obqScreen.Clear;
Out.String("You (");
Out.Char(human);
Out.String(") are in a high voltage maze with ");
Out.Int(machines,0);Out.Ln;
Out.String("security machines (");
Out.Char(machine); Out.String(") trying to kill you.");Out.Ln;
Out.String("You must maneuver them into the maze (");
Out.Char(fence);Out.Char(")");Out.Ln;
Out.String("to survive.");Out.Ln;
Out.Ln;
Out.String("Good luck!");Out.Ln;
Out.Ln;
Out.String("The movement keys are the following 8 digits:");Out.Ln;
Out.Ln;
obqScreen.Tab(10);Out.String(" \ ^ / ");Out.Ln;
obqScreen.Tab(10);Out.String(" \ | / ");Out.Ln;
obqScreen.Tab(10);Out.String(" 7 8 9 ");Out.Ln;
obqScreen.Tab(10);Out.String(" <--4 6-->");Out.Ln;
obqScreen.Tab(10);Out.String(" 1 2 3 ");Out.Ln;
obqScreen.Tab(10);Out.String(" / | \ ");Out.Ln;
obqScreen.Tab(10);Out.String(" / V \ ");Out.Ln;
Out.Ln;
Out.String("Plus '0' to end the game.");Out.Ln;
Out.Ln;
Out.String("Press any key to start.");
WHILE Input0.Available() = 0 DO
END;
END Instructions;
PROCEDURE Yes(question: ARRAY OF CHAR): BOOLEAN;
(*
Display the given string `question` followed by the valid answer
keys and wait for a valid keypress ("Y" or "N", case-insensitive).
If the key is "Y" return `TRUE`, otherwise return `FALSE`.
*)
VAR key: CHAR;
BEGIN
Out.String(question);
Out.String(" (Y/N)");
Out.Ln;
REPEAT
key := obqChar.Upper(obqKey.Wait());
UNTIL (key = "Y") OR (key = "N");
RETURN key = "Y"
END Yes;
PROCEDURE PrintMap;
(*
Display the map at the top left corner of the screen.
*)
VAR x,y: INTEGER;
BEGIN
obqScreen.Home;
FOR y := 0 TO lastY DO
FOR x := 0 TO lastX DO
Out.Char(map[y,x])
END;
Out.Ln;
END;
END PrintMap;
PROCEDURE IsBorder(y,x: INTEGER): BOOLEAN;
(*
If the given map coordinates `y` and `x` are part of the border map
(i.e. its surrounding fence), return `TRUE`, otherwise return
`FALSE`.
*)
BEGIN
RETURN (y = 0) OR (x = 0) OR (y = lastY) OR (x = lastX)
END IsBorder;
PROCEDURE Place(VAR y,x: INTEGER; c: CHAR);
(*
Place the given character `c` at a random empty position of the map
and update the given variables `y` and `x` with that position.
*)
VAR tmpY,tmpX: INTEGER;
BEGIN
REPEAT
tmpY := obqRandom.IntRange(1,lastY-1);
tmpX := obqRandom.IntRange(1,lastX-1);
UNTIL map[tmpY,tmpX] = empty;
y := tmpY;
x := tmpX;
map[y,x] := c
END Place;
PROCEDURE InhabitMap;
(*
Occupy the map with the machines, the inner fences and the human.
*)
VAR n,x,y: INTEGER;
BEGIN
FOR n := 0 TO lastMachine DO
Place(machineY[n],machineX[n],machine);
operative[n] := TRUE
END;
FOR n := 1 TO fences DO
Place(y,x,fence); (* XXX TODO *)
END;
Place(humanY,humanX,human);
END InhabitMap;
PROCEDURE MakeMap;
(*
Make an empty map with a surrounding fence.
*)
VAR x,y: INTEGER;
BEGIN
FOR y := 0 TO lastY DO
FOR x := 0 TO lastX DO
IF IsBorder(y,x) THEN
map[y,x] := fence
ELSE
map[y,x] := empty
END;
END;
END;
END MakeMap;
PROCEDURE Init;
(*
Init the game by making a new map and resetting the variables.
*)
BEGIN
MakeMap;
InhabitMap;
destroyedMachines := 0;
theEnd := 0;
END Init;
PROCEDURE MoveMachine(m: INTEGER);
(*
Move the given machine `m`.
*)
VAR
maybe: INTEGER;
BEGIN
map[machineY[m],machineX[m]] := empty;
maybe := obqRandom.IntRange(0,1);
IF machineY[m]>humanY THEN
DEC(machineY[m],maybe)
ELSIF machineY[m]<humanY THEN
INC(machineY[m],maybe)
END;
maybe := obqRandom.IntRange(0,1);
IF (machineX[m]>humanX) THEN
DEC(machineX[m],maybe)
ELSIF (machineX[m]<humanX) THEN
INC(machineX[m],maybe)
END;
IF map[machineY[m],machineX[m]] = empty THEN
map[machineY[m],machineX[m]] := machine
ELSIF map[machineY[m],machineX[m]] = fence THEN
operative[m] := FALSE;
INC(destroyedMachines);
IF destroyedMachines = machines THEN
theEnd := victory
END
ELSIF map[machineY[m],machineX[m]] = human THEN
theEnd := killed
END;
END MoveMachine;
PROCEDURE MoveMachines;
(*
Move all of the operative machines.
*)
VAR machine: INTEGER;
BEGIN
FOR machine := 0 TO lastMachine DO
IF operative[machine] & (obqRandom.IntMax(machinesDrag) = 0) THEN
MoveMachine(machine)
END;
END;
END MoveMachines;
PROCEDURE GetMove(VAR yInc,xInc: INTEGER): BOOLEAN;
(*
Read a keypress and update the given variables `yInc` and `xInc`
according to the direction, with values -1..1.
If the key is "0", update the `theEnd`.
Return `TRUE` if a movement key was pressed, otherwise `FALSE`.
*)
VAR key: CHAR;
BEGIN
yInc := 0;
xInc := 0;
Input0.Read(key);
CASE ORD(key) OF
48: theEnd := quit | (* "0" = quit *)
49: yInc := +1; xInc := -1 | (* "1" = SW *)
50: yInc := +1; | (* "2" = S *)
51: yInc := +1; xInc := +1 | (* "3" = SE *)
52: xInc := -1 | (* "4" = W *)
54: xInc := +1 | (* "6" = E *)
55: yInc := -1; xInc := -1 | (* "7" = NW *)
56: yInc := -1; | (* "8" = N *)
57: yInc := -1; xInc := +1 | (* "9" = NE *)
0..47,53,58..255: (* other = nothing *)
END;
RETURN (yInc # 0) OR (xInc # 0)
END GetMove;
BEGIN
obqRandom.Randomize;
obqScreen.Clear;
Out.String("CHASE");
Out.Ln;
IF Yes("Do you want instructions?") THEN
Instructions;
END;
REPEAT (* game loop *)
obqScreen.Clear;
Init;
REPEAT (* action loop *)
PrintMap;
moving := GetMove(yInc,xInc);
IF theEnd = 0 THEN
IF moving THEN
map[humanY,humanX] := empty;
IF map[humanY+yInc,humanX+xInc] = fence THEN
theEnd := electrified
ELSIF map[humanY+yInc,humanX+xInc] = machine THEN
theEnd := killed
ELSE
map[humanY,humanX] := empty;
humanY := humanY + yInc;
humanX := humanX + xInc;
map[humanY,humanX] := human;
PrintMap;
MoveMachines
END;
END;
END;
UNTIL theEnd # 0; (* action loop *)
CASE theEnd OF
quit:
Out.String("Sorry to see you quit.") |
electrified:
Out.String("Zap! You touched the fence!") |
killed:
Out.String("You have been killed by a lucky machine.") |
victory:
Out.String("You are lucky, you destroyed all machines.");
END;
Out.Ln;
UNTIL ~ Yes("Do you want to play again?"); (* game loop *)
Out.String("Hope you don't feel fenced in.");
Out.Ln;
Out.String("Try again sometime.");
Out.Ln;
END chase.
Sinewave
(*
Sine Wave
Original version in BASIC:
Creative Computing (Morristown, New Jersey, USA), ca. 1980.
This version in Oberon-07:
Copyright (c) 2022, 2023, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Last modified 2020231103T2153+0100.
*)
MODULE sinewave;
IMPORT
In,
Math,
Out,
obqKey,
obqScreen;
VAR
step: INTEGER;
key: CHAR;
angle: REAL;
word: ARRAY 2,80 OF CHAR;
BEGIN
obqScreen.Clear;
Out.String("Sine Wave");
Out.Ln;
Out.Ln;
Out.String("Original version in BASIC:");
Out.Ln;
obqScreen.Tab(4);
Out.String("Creative computing (Morristown, New Jersey, USA), ca. 1980.");
Out.Ln;
Out.String("This version in Oberon:");
Out.Ln;
obqScreen.Tab(4);
Out.String("Copyright (c) 2022, 2023, Marcos Cruz (programandala.net)")
Out.Ln;
Out.String("SPDX-License-Identifier: Fair")
Out.Ln;
Out.Ln;
Out.String("Press any key to start the program.");
Out.Ln;
key := obqKey.Wait();
obqScreen.Clear;
Out.String("Enter the first word: ");
Out.Ln;
In.Line(word[0]);
Out.String("Enter the second word: ");
Out.Ln;
In.Line(word[1]);
obqScreen.Clear;
step := 0;
angle := 0.0;
WHILE angle<40.0 DO
obqScreen.Tab(FLOOR(26.0+25.0*Math.sin(angle)));
Out.String(word[ORD(ODD(step))]);
Out.Ln;
INC(step);
angle := angle+0.25;
END
END sinewave.
