Version 4.1 now available

C# <1kB RogueLike v4

openSUSE Linux

screenshot of C# 1kB RougueLike running in Linux

Windows

screenshot of C# 1kB RougueLike running in Windows

What is it?

A RogueLike game written in less than 1kB of C# source. Versions 1 through 3 were written for the rec.games.roguelike.development <1kB RL Challenge:

First <1kb RL Challenge

A year or so after the challenge a <512byte Roguelike was posted to the group. This brought about a renewed interest, with others posting new 1kB RogueLikes. I decided to revisit mine and see if any further optimizations could be made, allowing me to add more features.

Note that for the challenge we were allowed to use a separate IO library, such as curses. I used a custom IO lib, which is included below.

Features:

Download:

1kRl Visual Studio Solution (zipped)

1kRl.exe

Program.cs

ProgramPretty.cs - Pretty printed version with comments

IO.cs

Instructions:

Any key to begin; arrow keys, numpad keys or vi keys to move, q or [ESC] to quit. The current dungeon level and your health are shown at the bottom. If your health reaches 0 then the game is over.

Source:

using System;using S=System.String;using System.Collections.Generic;partial class M{Dictionary<S,S>d=new Dictionary<S,S>();List<S>e=new List<S>();int p=9,m=999,x,y,z,u,v,i,j;S a="@",w="#",f=".",s=">",c;Random r=new Random();M(int k,int l,int h){u=m*l;while(i++<u){e.Add((r.Next(m)-499)+a+(r.Next(m)-499));d[e[e.Count-1]]="M";}d[X]=a;while(k>0){u=x;v=y;x=k>2&&k<20?x-1:k>12?x+1:x;y=k%2>0&&k%10!=0?y-1:k%2==0&&k%10!=0?y+1:y;c=T(X);if(e.Contains(X)){e.Remove(X);d[X]=f;}R();if(c==s){d.Clear();e.Clear();new M(1,l+1,h);return;}if(c!=f){x=u;y=v;}d[u+a+v]=f;d[X]=a;for(z=0;z<e.Count;z++){i=U(e[z],0);j=U(e[z],1);u=i<x?i+1:i>x?i-1:i;v=j<y?j+1:j>y?j-1:j;c=T(u+a+v);if(c==f){e[z]=u+a+v;d[e[z]]=d[i+a+j];d[i+a+j]=f;}if(c==a)h--;}if(h<1)return;z=p*2-1;for(j=0;j<z;j++){for(i=0;i<z;i++){c=T((i-p+x)+a+(j-p+y));W(c,c==w?l:0);}L();}L(l+f+h);k=I;}}S X{get{return x+a+y;}}S T(S c){return d.ContainsKey(c)?d[c]:d[c]=r.Next(m*p)<5?s:r.Next(9)<7?f:w;}int U(S c,int l){return int.Parse((c.Split('@'))[l]);}static void Main(){new M(1,1,9);}}

Pretty printed source:

using System;

using S = System.String;

using System.Collections.Generic;

partial class M {

  Dictionary<S, S> d = new Dictionary<S, S>( );

  List<S> e = new List<S>( );

  int p = 9, m = 999, x, y, z, u, v, i, j;

  S a = "@", w = "#", f = ".", s = ">", c;

  Random r = new Random( );

 

  M( int k, int l, int h ) {

    //determine number of enemies

    u = m * l;

 

    //add enemies

    while ( i++ < u ) {

      e.Add( ( r.Next( m ) - 499 ) + a + ( r.Next( m ) - 499 ) );

      d[ e[ e.Count - 1 ] ] = "M";

    }

 

    //set player start

    d[ X ] = a;

 

    //k will be 0 if player presses Q or ESC

    while ( k > 0 ) {

      //store player co-ords

      u = x;

      v = y;

 

      //handle keyboard return code

      x = k > 2 && k < 20 ? x - 1 : k > 12 ? x + 1 : x;

      y = k % 2 > 0 && k % 10 != 0 ? y - 1 : k % 2 == 0 && k % 10 != 0 ? y + 1 : y;

 

      //get the string at the new location

      c = T( X );

 

      //if player is hitting an enemy

      if ( e.Contains( X ) ) {

        //kill the enemy

        e.Remove( X );

        d[ X ] = f;

      }

 

      //reset the cursor position

      R( );

 

      //if the player is standing on stairs

      if ( c == s ) {

        //reset the dungeon and enemies

        d.Clear( );

        e.Clear( );

        //new dungeon

        new M( 1, l + 1, h );

        //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 ( c != 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[ X ] = a;

 

      //iterate through the remaining enemies

      for ( z = 0; z < e.Count; z++ ) {

        //get the current enemy's location and store their X position in i and

        //their Y position in j

        i = U( e[ z ], 0 );

        j = U( e[ z ], 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 dungeon tile at the new enemy coordinate

        c = T( u + a + v );

 

        //if the dungeon tile is floor, move the enemy onto the new coordinate

        if ( c == f ) {

          e[ z ] = u + a + v;

          d[ e[ z ] ] = d[ i + a + j ];

          d[ i + a + j ] = f;

        }

 

        //if the dungeon tile the enemy tried to move to contains the player,

        //decrement the player's health

        if ( c == a ) h--;

      }

 

      //exit if player is dead

      if ( h < 1 ) return;

 

      //calculate the viewport size using p

      z = p * 2 - 1;

 

      //draw visible dungeon tiles to viewport

      for ( j = 0; j < z; j++ ) {

        for ( i = 0; i < z; i++ ) {

          //get tile at location X = i, Y = j

          c = T( ( i - p + x ) + a + ( j - p + y ) );

          //if it's a wall pass the level through to the IO call for the color,

          //otherwise pass through 0 to use default color

          W( c, c == w ? l : 0 );

        }

        L( );

      }

      //show level and health

      L( l + f + h );

      //get keyboard input

      k = I;

    }

  }

 

  //return the player's coordinates as a string

  S X {

    get { return x + a + y; }

  }

 

  //get the dungeon tile at the passed in coordinate. If the tile doesn't exist

  //create it

  S T( S c ) {

    return d.ContainsKey( c ) ? d[ c ] : d[ c ] = r.Next( m * p ) < 5 ? s : r.Next( 9 ) < 7 ? f : w;

  }

 

  //convert a string coordinate to an x or a y coordinate - if l is 0, return

  //x, if l is 1 return y

  int U( S c, int l ) { return int.Parse( ( c.Split( '@' ) )[ l ] ); }

 

  //start a new game on level 1 with 9 health

  static void Main( ) { new M( 1, 1, 9 ); }

}

IO Source:

using System;using C=System.Console;using K=System.ConsoleKey;using O=System.ConsoleColor;partial class M{void W(string s,int c){if(c==0)C.ForegroundColor=(O)7;else{while(c>15)c-=15;C.ForegroundColor=(O)c;}C.Write(s);}void L(){L("");}void L(object o){C.ForegroundColor=(O)15;C.WriteLine(o);}void R(){C.BackgroundColor=0;C.SetCursorPosition(0,0);}static int I{get{switch((C.ReadKey(true)).Key){case K.Q:case K.Escape:return 0;case K.LeftArrow:case K.NumPad4:case K.H:return 10;case K.RightArrow:case K.NumPad6:case K.L:return 20;case K.UpArrow:case K.NumPad8:case K.K:return 1;case K.DownArrow:case K.NumPad2:case K.J:return 2;case K.NumPad7:case K.Y:return 11;case K.NumPad9:case K.U:return 21;case K.NumPad1:case K.B:return 12;case K.NumPad3:case K.N:return 22;}return 5;}}}

Historic versions:

  1. v1
  2. v2
  3. v3