Results 1 to 5 of 5

Thread: Random level generation.

  1. #1

    Random level generation.

    Hey guys, and gal(s), I've been working on some "blind" level generation. Right now the stuff is still just a prototype that uses a string grid to make a level. The explanation is better in code as to how I'm doing it, so I won't bother to state anything special here, but here's a dirty preview. Walls are pound signs (#) and floors are periods (.), while the entry and exit are less-than (<) and greater-than (>) symbols, just in case you couldn't decipher them all.

    Code:
    ##################################################
    ##########...........#.........................###
    ##########.##...#.#....#......####.#..<..........#
    ##########.##........#.#.#.######..#.........#..##
    ##########.##..##................................#
    #.########.##..##..............................#.#
    #.########.##..####.........#####.#.#..##.######.#
    #.########.##..####....#.###...........##.######.#
    #.########.##....##..###.########.......#.######.#
    #.########....##.##....#.#########.....##.######.#
    #.##########..##.##......#########.######.####...#
    #.##########..##......############.######.######.#
    #######>......##########..########.######.########
    #######.###...##########.#########.######.########
    #######.###...#####......#########.######.########
    #######.###...#####................######.########
    #######.###...####...................####.....####
    #######.###...........#######################.####
    #######.###..########.#######################.####
    #######.###..########.#######################....#
    #######.####.########.##########################.#
    #######...........###.########################...#
    #######.####.####.###.#############..............#
    #######.####......#...############################
    ##################################################
    Code:
    ##################################################
    #............................................#####
    ######.#######.##..........#######..########.#####
    ######.#######.##.#.....##.#######..########.#####
    ######.#######.##.#......#.#........########.#####
    ##############.##...#..#.#...<.....#########.#####
    ########.#.............#...#.#####.###############
    #.................#.#....#.#.#####################
    #.########.########.#.##.#.#.#####################
    #.########.########.#....#.#.#####################
    #.#################.#......#......################
    #.#################.#############.################
    #.#################.........#####.#######.####.###
    #.##...############.####.##.#####.#######.####.###
    #.##.#.#######..###.####.##.##.##.........####.###
    #.##.#.#######......####.#..##..##############.###
    #.##.#.########.##..####.#.##.#.##############.###
    #...............##..............##############...#
    ####.#.########.............#.#...##############.#
    #.##.#.######...................#.##############.#
    #>.....###########.#######...####.##########.....#
    #.##.#.########..........#...####.##########.###.#
    #.##.........##.########.#...#....##########.##..#
    #......#####....########.....#.................###
    ##################################################
    Currently the application fills in a third of the level with a 75% chance to avoid a floor tile that is 2 units away. It only moves in the primary n/s/e/w directions, though I need to fix their directional modifiers because they're inverted. East is West and so on. While generating the level a log is shown to track down some very bizarre errors.

    Basically, any advice on improvement? It's really dirty work that I've only been doing for about two days tops now. At first it was entirely random in the for loop, then later a little logic got added, and then it was revamped into the following. I also coded a copy button so you could see the results and learned at the same time that I hate the windows clipboard. Download the binary and mess with it for a few minutes and see what the levels look like more than just the one preview.

    Full download is here, source to the main unit follows. (Updated: 3:25PM EST [-700 GMT])

    [pascal]unit uMain;

    interface

    uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls, ExtCtrls, Grids, Buttons, Clipbrd, uMessages, Math;

    type
    tTile = record
    pic: char;
    odds: byte;
    end;
    TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    Panel1: TPanel;
    Button1: TButton;
    Label1: TLabel;
    Cols: TEdit;
    Rows: TEdit;
    Label2: TLabel;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure DimensionKeyPress(Sender: TObject; var Key: Char);
    procedure FormResize(Sender: TObject);
    procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
    Rect: TRect; State: TGridDrawState);
    private
    { Private declarations }
    public
    { Public declarations }
    end;

    TDirection = (dNorth,dNorthWest,dWest,dSouthWest,dSouth,dSouthE ast,dEast,dNorthEast);
    TDirections = set of TDirection;

    const
    DirTransforms: array[TDirection,0..1] of smallint = ((0,1),(1,1),(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1));
    DirNames: array[TDirection] of shortstring = ('North', 'Northwest', 'West', 'SouthWest',
    'South', 'Southeast', 'East', 'Northeast');

    var
    Form1: TForm1;

    procedure turn(var dir: TDirection; deg: smallint);

    implementation

    {$R *.dfm}

    // Turn(dir = dNorth, 2) = dWest, Turn(dir = dNorth, -2) = dEast
    procedure turn(var dir: TDirection; deg: smallint);
    begin
    if deg <0>= 0.50 then
    result := 1
    else
    result := -1;
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    var dx, dy, x, y, tx, ty, sx, sy, fx, fy: integer;
    floorspace: integer;
    validdirs: TDirections;
    dir: TDirection;
    begin
    StringGrid1.DefaultDrawing := False;

    with frmStatus do begin
    if not Visible then
    Show
    else
    BringToFront;

    Entries.Clear;
    end;

    if TryStrToInt(Cols.Text,dx) and TryStrToInt(Rows.Text,dy) then begin
    Randomize;
    Assert((dx > 4) and (dy > 4));

    StringGrid1.ColCount := dx;
    StringGrid1.RowCount := dy;
    validdirs := [dNorth,dWest,dEast,dSouth];
    dir := dNorthWest;
    floorspace := (dx*dy) div 3;

    frmStatus.Log('Initializing...');
    frmStatus.Log(format('RowCount: %d',[dy]));
    frmStatus.Log(format('ColCount: %d',[dx]));
    frmStatus.Log(format('Floorarea: %d',[floorspace]));

    for y := 0 to dy - 1 do
    for x := 0 to dx - 1 do
    StringGrid1.Cells[x,y] := '#';

    while not (dir in validdirs) do
    dir := TDirection(Random(Byte(High(TDirection))+1));

    x := 1 + Random(dx-2);
    y := 1 + Random(dy-2);

    frmStatus.Log(Format('Starting point (%d,%d)',[x,y]));

    while floorspace > 0 do begin
    validdirs := [dNorth,dWest,dEast,dSouth];

    if (floorspace >= 5) and (random(100) < 2) then begin
    repeat
    x := 1 + Random(dx-2);
    y := 1 + Random(dy-2);
    until (StringGrid1.Cells[x,y] = '#');
    while not (dir in validdirs) do
    dir := TDirection(Random(Byte(High(TDirection))+1));
    end;

    if x <2> dx-3 then
    Exclude(validdirs,dWest);

    if y <2> dy-3 then
    Exclude(validdirs,dNorth);

    if (random(100) < 15) then
    repeat
    turn(dir,2*randomSign);
    until (dir in validdirs);

    tx := (DirTransforms[dir][0]*2);
    ty := (DirTransforms[dir][1]*2);

    // here we check to see the the approaching tile two ahead is floor, and
    // then probably turn away from it. It's better than having a ton of huge
    // rooms or identical passages.
    if (x+tx in [1..dx-2]) and (y+ty in [1..dy-2]) then
    if (StringGrid1.Cells[x+tx,y+ty] = '.') and (Random(100) <70> (Sqrt(Sqr(dx) + Sqr(dy)) * 0.55) then
    Break;
    until (false);

    frmStatus.Log(Format('Entry point ("<") at (%d,%d)',[sx,sy]));
    frmStatus.Log(Format('Exit point (">") at (%d,%d)',[fx,fy]));
    StringGrid1.Cells[sx,sy] := '<';
    StringGrid1.Cells[fx,fy] := '>';
    end else
    ShowMessage('Error, one of the dimensions is not an integer.');

    BringToFront;
    end;

    procedure TForm1.DimensionKeyPress(Sender: TObject; var Key: Char);
    begin
    if not (Key in ['0'..'9',Char(VK_BACK)]) then
    Key := #0;
    end;

    procedure TForm1.FormResize(Sender: TObject);
    begin
    Button2.Left := Panel1.Width - 78;
    end;

    procedure TForm1.SpeedButton1Click(Sender: TObject);
    var sl: TStringList;
    x,y: integer;
    s: string;
    begin
    sl := TStringList.Create;

    for y := 0 to StringGrid1.RowCount - 1 do begin
    s := '';
    for x := 0 to StringGrid1.ColCount - 1 do
    s := s + StringGrid1.Cells[x,y][1];
    sl.Add(s);
    end;

    with Clipboard do begin
    Open;
    SetTextBuf(PChar('
    Code:
    '+Sl.Text+'
    '));
    Close;
    end;

    sl.Free;
    end;

    procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
    Rect: TRect; State: TGridDrawState);
    var x,y: integer;
    begin
    if TStringGrid(Sender).DefaultDrawing then
    Exit;

    with sender as TStringGrid do begin
    // if gdSelected in State then
    // Brush.Color := clHighlight
    // else
    // Brush.Color := clWindow;
    // Canvas.FillRect(Rect);

    if trim(Cells[ACol,ARow]) = '' then exit;

    x := ((Rect.Right - Rect.Left) - Canvas.TextWidth(Cells[ACol,ARow])) div 2;
    y := ((Rect.Bottom - Rect.Top) - Canvas.TextHeight(Cells[ACol,ARow])) div 2;

    if Cells[ACol,ARow][1] in ['<','>'] then begin
    Canvas.Font.Color := clRed;
    Canvas.Font.Style := [fsBold];
    end else begin
    if gdSelected in State then
    Canvas.Font.Color := clHighlightText
    else
    Canvas.Font.Color := clWindowText;
    Canvas.Font.Style := [];
    end;

    Canvas.TextRect(Rect,Rect.Left+x,Rect.Top+y,Cells[ACol,ARow]);
    end;
    end;

    end.[/pascal]

  2. #2

    Random level generation.

    New version supports entry(<)/exit(>) points, renders those points as bold red characters so they're easier to see, and is just generally more tricky in how the level is generated.

    The download has been updated in the first post, as well as the source code.

    Any ideas for better performance, aside from the performance costs of logging the actions for debugging?

    Code:
    ###########################################################################
    #..##......................####...........................########.########
    #..##.#.................................#.#........................########
    #..##.#.#############.#############.....#.##.#########...#########.########
    #..##....#.....................#######....................................#
    #..####..#.##########.########.#######.##.##.#...........#########.######.#
    #..####..#.#.########...............................###..#########.######.#
    #..####..#.#.#################.#######.##..#.##....................####...#
    #..####..#.#...............###.####..........##.####.##..########..####.#.#
    #..####..#.#.##.#..###########.####.....#..####.####.##............####.#.#
    #........#.#.>.....###########.######.###..####.####.....####...........#.#
    #..........#....#..###########.######......####.################.##########
    ##...........####..###########..##########.####.#######............########
    ##............###............######........####.##########....##...########
    ########......####.##########...................##########......<.#########
    ########.#########.#############.#.....##.....###############.....#########
    ########.#########.#############.#..........#.#############################
    ########.......###.#############.....#.#...##.#############################
    ##################.#############.......###....#############################
    ##################.#############.....#####..........#######################
    ##################.###############.##.......###############################
    ##################.#######################.....############################
    #################..########################################################
    #################..########################################################
    ###########################################################################

  3. #3

    Random level generation.

    I know it has been a long time since you have posted this... I was trying to compile your code and I was getting an error with it... I was hopeing to make some changes so that it was saved to disk...
    http://dexrow.blogspot.com/2007/09/land-of-cigo-roguelike-w-source.html

  4. #4

    Random level generation.

    Well, what problems were you getting? I recently reformatted and I've yet to reinstall Delphi, but I can still help you.

  5. #5

    Random level generation.

    StringGrid1.ExplicitLeft: Property ExplicitLeft does not exist.
    and same for:
    ExplicitTop
    ExplicitHeight
    ExplicitWidth

    FIX:
    open uMain AND uMessages forms, ignore warnings of missing properties, save, compile and run

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •