Skip to content
Game 15 Unit 8 of 32 1 hr learning time

The Viewport

Display a 7x7 window centred on the player instead of the full grid — the dungeon scrolls with every step.

25% of Dungeons Of Dorin

Showing the entire 16x16 grid reveals everything at once — no mystery, no discovery. A viewport changes that. Instead of the full dungeon, the player sees only a 7x7 window centred on their position. As they move, the window moves with them, revealing new cells and hiding old ones. The dungeon becomes something to explore rather than something to survey.

The Program

5 REM === DUNGEONS OF DORIN ===
10 BORDER 0: PAPER 0: INK 7: CLS
20 PRINT AT 3, 4; INK 7; BRIGHT 1; "DUNGEONS OF DORIN"
30 PRINT AT 6, 2; INK 6; "Descend ten floors of"
35 PRINT AT 7, 2; INK 6; "darkness. Find the Heart."
40 PRINT AT 10, 2; INK 7; "Q/A = up/down"
42 PRINT AT 11, 2; INK 7; "O/P = left/right"
44 PRINT AT 14, 2; INK 7; "Press any key to begin"
50 PAUSE 0
60 CLS
70 REM === define stats ===
80 LET h = 20: LET z = 20: LET a = 3: LET f = 2
90 REM === generate floor ===
92 GO SUB 1000
94 LET x = u(1,1) + 1: LET y = u(1,2) + 1
96 GO SUB 300
98 REM === main loop ===
100 PAUSE 0: LET i$ = INKEY$
102 LET t = x: LET t2 = y
104 IF i$ = "q" OR i$ = "Q" THEN LET t2 = y - 1
106 IF i$ = "a" OR i$ = "A" THEN LET t2 = y + 1
108 IF i$ = "o" OR i$ = "O" THEN LET t = x - 1
110 IF i$ = "p" OR i$ = "P" THEN LET t = x + 1
112 IF t = x AND t2 = y THEN GO TO 98
114 IF t < 1 OR t > 16 OR t2 < 1 OR t2 > 16 THEN GO TO 98
116 IF d(t2,t) = 1 THEN GO TO 98
118 LET x = t: LET y = t2
120 GO SUB 300
122 GO TO 98
300 REM === draw viewport ===
302 FOR c = -3 TO 3
304 FOR o = -3 TO 3
306 LET t = x + o: LET t2 = y + c
308 LET i = 1: LET i2 = 0
310 IF t < 1 OR t > 16 OR t2 < 1 OR t2 > 16 THEN GO TO 340
312 LET i = d(t2,t)
314 LET i2 = 7
316 IF i = 1 THEN LET i2 = 4
340 LET t3 = 3 + c: LET t4 = 11 + o
342 IF o = 0 AND c = 0 THEN LET i2 = 7: LET i = 6
344 IF i2 = 0 THEN PRINT AT t3, t4; " ": GO TO 370
346 IF i = 0 THEN PRINT AT t3, t4; INK i2; ".": GO TO 370
348 IF i = 1 THEN PRINT AT t3, t4; INK i2; CHR$ 143: GO TO 370
350 IF i = 6 THEN PRINT AT t3, t4; INK i2; "@": GO TO 370
352 PRINT AT t3, t4; INK i2; "?"
370 NEXT o: NEXT c
372 RETURN
1000 REM === generate floor subroutine ===
1002 DIM d(16,16)
1004 FOR i = 1 TO 16: FOR o = 1 TO 16
1006 LET d(i,o) = 1
1008 NEXT o: NEXT i
1010 LET s = INT (RND * 3) + 3
1012 DIM u(5,4)
1014 FOR i = 1 TO s
1016 LET u(i,1) = INT (RND * 10) + 2
1018 LET u(i,2) = INT (RND * 10) + 2
1020 LET u(i,3) = INT (RND * 4) + 3
1022 LET u(i,4) = INT (RND * 4) + 3
1024 FOR c = u(i,2) TO u(i,2) + u(i,4) - 1
1026 FOR o = u(i,1) TO u(i,1) + u(i,3) - 1
1028 IF c >= 1 AND c <= 16 AND o >= 1 AND o <= 16 THEN LET d(c,o) = 0
1030 NEXT o: NEXT c
1032 NEXT i
1034 FOR i = 1 TO s - 1
1036 LET t = u(i,1) + INT (u(i,3) / 2)
1038 LET t2 = u(i,2) + INT (u(i,4) / 2)
1040 LET t3 = u(i+1,1) + INT (u(i+1,3) / 2)
1042 LET t4 = u(i+1,2) + INT (u(i+1,4) / 2)
1044 IF t <= t3 THEN FOR o = t TO t3: IF t2 >= 1 AND t2 <= 16 AND o >= 1 AND o <= 16 THEN LET d(t2,o) = 0
1046 IF t <= t3 THEN NEXT o
1048 IF t > t3 THEN FOR o = t3 TO t: IF t2 >= 1 AND t2 <= 16 AND o >= 1 AND o <= 16 THEN LET d(t2,o) = 0
1050 IF t > t3 THEN NEXT o
1052 IF t2 <= t4 THEN FOR c = t2 TO t4: IF c >= 1 AND c <= 16 AND t3 >= 1 AND t3 <= 16 THEN LET d(c,t3) = 0
1054 IF t2 <= t4 THEN NEXT c
1056 IF t2 > t4 THEN FOR c = t4 TO t2: IF c >= 1 AND c <= 16 AND t3 >= 1 AND t3 <= 16 THEN LET d(c,t3) = 0
1058 IF t2 > t4 THEN NEXT c
1060 NEXT i
1062 RETURN

How It Works

Lines 300-372 are the viewport rendering subroutine. Two nested loops run from -3 to +3, creating a 7x7 grid centred on the player.

Lines 306-308 calculate the world coordinates for each viewport cell. The offset o and c are added to the player position to find the actual dungeon cell being displayed.

Line 310 checks whether the world coordinate is within the grid. Cells outside the 16x16 boundary are treated as walls — the dungeon is surrounded by impenetrable darkness.

Lines 340-342 calculate the screen position for each cell. The viewport is drawn at a fixed position on screen, with the player always at the centre (row 3, column 11 in this layout).

Line 342 draws the player as a white @ at the centre of the viewport. The player symbol always appears at the same screen position — it is the world that moves around them.

Lines 344-358 draw each cell type with a different character and colour. Walls are green blocks (INK 4, CHR$ 143). Floor is white dots. Stairs show as >. Chests show as $. Cells outside the grid or unseen cells show as blank spaces.

Offset Calculation

The key insight is the relationship between viewport position and world position. For each cell in the 7x7 viewport at offset (o, c) from centre, the world coordinate is (x+o, y+c). The screen coordinate is fixed — the viewport sits at the same place on screen regardless of where the player is in the dungeon.

This separation between world space and screen space is fundamental. The world is the 16x16 grid. The screen is the 7x7 viewport. The player position connects them. Move the player and the viewport shifts, showing different world cells at the same screen positions.

Edge Clipping

When the player is near the edge of the grid, some viewport cells fall outside the 16x16 boundary. These cells are drawn as empty space — solid black. The bounds check on line 310 prevents array access errors by skipping the dungeon lookup for out-of-bounds cells. The edge of the dungeon appears as void, reinforcing the sense that the player is underground in a bounded space.

Try This

Change the viewport size. Use -4 to +4 for a 9x9 viewport, or -2 to +2 for a 5x5 one. A larger viewport reveals more but reduces tension. A smaller one is claustrophobic but makes exploration slower.

Move the viewport position. Shift it to the left side of the screen by changing the column offset in line 340. This leaves room on the right for a larger status panel.