using System;
using System.Collections.Generic;
using S = System.String;
// Naming guide:
//
// CLASSES:
// S = (S)tring
// C = (C)onsole
// L = (L)evel
//
// int:
// d = (d)ungeon
// p = view(p)ort radius
// b = (b)ig number
// x = player (x) position
// y = player (y) position
// g = (g)eneral temporary storage
// c = last (c)haracter pressed
// l = (l)evel
// h = (h)ealth
// k = (k)ills
//
// string:
// a = (a)t
// w = (w)all
// f = (f)loor
// s = (s)tairs
// m = (m)onster
// t = (t)ile OR (t)ile location
//
// other variables:
// r = (r)andom number provider
// e = (e)nemy list
//
// properties:
// P = current player (P)osition
//
// methods:
// S T( S c ) = (S)tring representing dungeon (T)ile at (S)tring (c)oordinate
// int V( S c, int i ) = con(V)ert (S)tring (c)oordinate to an int x or y coordinate using (i)ndex, 0 for x, 1 for y
///
/// represents a level of the dungeon
///
partial class L {
// d stores the tiles that the player has seen, the Key is a
// string representing the X,Y coordinates for a tile in the format "X@Y" ie
// "4@12" and the Value is the string to use to draw the tile
Dictionary d = new Dictionary( );
// p is used to set the viewport size (viewport size is p*2-1)
// b is used in the calculations for number of enemies and also in the
// dungeon generation
// x and y store the player's current location, these shouldn't be changed
// unless the player moves
// g is used as a general purpose variable for counters and temporary storage
// u & v and i & j (no real meaning) are used to store temporary X and Y
// values and also as general purpose variables when needed
// c stores an integer representation of the last key pressed
int p = 9, b = 999, x, y, g, u, v, i, j, c;
// a is the string to use for the player, also used as seperator in string
// representations of X,Y locations
// w is the string to use for walls
// f is the string to use for floors
// s is the string to use for stairs
// m is the string to use for monsters
// t is used to store a given tile from the dungeon, for example to test if
// the player can move onto that square, or a location in the form described
// above
S a = "@", w = "#", f = ".", s = ">", m = "M", t;
// random seed uses default value of system clock
Random r = new Random( );
///
/// Main game loop in the guise of a constructor - allows recursive calls
/// when going up a level
///
/// the current level
/// the player's health
/// the player's number of kills
L( int l, int h, int k ) {
// e is a list of enemy locations, using the "X@Y" format, which allows us
// to update the enemy on the map by changing the dungeon tiles using the
// enemy location as the lookup Key into d
var e = new List( );
//add enemies - at this point g hasn't been initialized so will start
//with default value for an int, 0. Number of enemies is b * the level * 2
//so gets harder as you descend
while ( g++ < b * l * 2 ) {
//get a random location within ~500 squares of the player
t = ( r.Next( b ) - 499 ) + a + ( r.Next( b ) - 499 );
//add an enemy at this location - might be same location as an existing
//enemy, oh well
e.Add( t );
//set the dungeon tile at the newly created enemy's position to m
d[ t ] = m;
}
//set dungeon tile at current player position to player string - "@"
d[ P ] = a;
//game loop
for ( ; ; ) {
//store player's current x and y location
u = x;
v = y;
//change c from being a number from 97-105 to being a number 1-9
//
//value before:
//NumPad1 = 97 = down + left
//NumPad2 = 98 = down
//NumPad3 = 99 = down + right
//NumPad4 = 100 = left
//NumPad6 = 102 = right
//NumPad7 = 103 = up + left
//NumPad8 = 104 = up
//NumPad9 = 105 = up + right
//
//value after:
//NumPad1 = 1 = down + left
//NumPad2 = 2 = down
//NumPad3 = 3 = down + right
//NumPad4 = 4 = left
//NumPad6 = 6 = right
//NumPad7 = 7 = up + left
//NumPad8 = 8 = up
//NumPad9 = 9 = up + right
g = c - 96;
//numbers for left are 1,4,7 - as these are all 3 apart we can add 2
//to get 3,6,9 then mod 3 this value to see if it's a left key
//otherwise, numbers for right are 3,6,9 so we can mod 3 these to see
//if they're a right key
x += ( g + 2 ) % 3 == 0 ? -1 : g % 3 == 0 ? 1 : 0;
//up is 7,8,9 and down is 1,2,3
y += g > 6 && g < 10 ? -1 : g > 0 && g < 4 ? 1 : 0;
//get the string at the new location
t = T( P );
//if player is hitting an enemy
if ( t == m ) {
//kill the enemy
e.Remove( P );
//set the tile where the enemy used to be to floor
d[ P ] = f;
//Increment kills
k++;
}
//reset the cursor position
R();
//if the player moved onto stairs
if ( t == s ) {
//new dungeon level, increment the current level
new L( l + 1, h, k );
//if we get here player has died, exit
return;
}
//if the player tried to move onto something that isn't a floor, put
//them back where they were
if ( t != f ) {
x = u;
y = v;
}
//set the last tile that the player was on to floor
d[ u + a + v ] = f;
//set the tile that the player is currently on to @
d[ P ] = a;
//iterate through the remaining enemies
for ( g = 0; g < e.Count; g++ ) {
//get the current enemy's location and store their X position in i and
//their Y position in j
i = V( e[ g ], 0 );
j = V( e[ g ], 1 );
//move towards the player
u = i < x ? i + 1 : i > x ? i - 1 : i;
v = j < y ? j + 1 : j > y ? j - 1 : j;
//get the potential new enemy coordinate
t = u + a + v;
//if the dungeon tile is floor, move the enemy onto the new coordinate
if ( T( t ) == f ) {
//change the enemy's location in the enemy list
e[ g ] = t;
//set the dungeon tile at the new location
d[ t ] = m;
//set the dungeon tile at the old location to floor
d[ i + a + j ] = f;
}
//if the dungeon tile the enemy tried to move to contains the player,
//decrement the player's health
if ( T( t ) == a ) h--;
}
//exit if player is dead
if ( h < 1 ) return;
//calculate the viewport size using p
g = p * 2 - 1;
//draw visible dungeon tiles to viewport
for ( j = 0; j++ < g; ) {
for ( i = 0; i++ < g; ) {
//get tile at location X = i, Y = j
t = T( ( i - p + x ) + a + ( j - p + y ) );
//if t is a wall then assign u the level number, otherwise assign it
//7 (gray)
u = t == w ? l : 7;
//we are using u for the color, but only colors in range 0-15 are
//valid, so if u is higher than 15, keep subtracting 15 until it
//isn't anymore
while ( u > 15 )
u -= 15;
//Set foreground color to u, or 7 (gray) if u is 0
F( u < 1 ? 7 : u );
//draw the current dungeon tile
W( t );
}
//start a new row
N( "" );
}
//set color to grayx
F( 7 );
//separate level, health and kills with a floor tile and draw below
//viewport
N( "L" + l + f + "H" + h + f + "K" + k );
//get keyboard input
c = I;
}
}
///
/// returns the player's coordinates as a string in the format "X@Y" ie "4@12"
///
S P {
get { return x + a + y; }
}
///
/// get the dungeon tile at coordinate c, if no tile exists there create it
///
/// a dungeon coordinate in the form "X@Y" ie "4@12"
/// a string representing a dungeon tile
S T( S c ) {
return d.ContainsKey( c ) ? d[ c ] : d[ c ] = r.Next( b * 9 ) < 9 ? s : r.Next( 9 ) < 7 ? f : w;
}
///
/// converts a string coordinate to either an X or Y coordinate
///
/// the string coordinate to convert
/// whether to return X or Y, 0 = X, 1 = Y
/// the X or Y location as an int
int V( S c, int i ) { return int.Parse( c.Split( '@' )[ i ] ); }
static void Main( ) {
//start a new game on level 1 with 9 health and 0 experience
new L( 1, 9, 0 );
//wait for newline before exiting
E();
}
}