Преобразование алгоритма подземелья из Java в JavaScript не работает - PullRequest
0 голосов
/ 01 сентября 2011

Я пытаюсь преобразовать этот алгоритм подземелий из java в javascript, однако мой сценарий работает в 70% случаев.Когда это работает, проблемы: в комнатах отсутствует стена с одной стороны, и к некоторым комнатам невозможно получить доступ каким-либо образом .Когда это не работает, оно застревает в бесконечном цикле.

room is missing a side

room is missing a side

room is missing a side

(Извинитеизображения маленькие, я только что обновил свой jfiddle, а вывод больше http://jsfiddle.net/gUmH7/1/)

Я предполагаю, что проблема в makeRoom (), а если нет, то в нем определенно createDungeon (). Поэтому, когда алгоритм работает,после первого вызова makeRoom () я получаю 1 и 2 в моем массиве dungeon_map, где 1 - коричневая стена, а 2 - желтый пол. Когда алгоритм не работает, в dungeon_map нет 1 или 2массив, приводящий к бесконечному циклу.

Я почти уверен, что Java-код работает, потому что здесь один с выходом онлайн . А вот original .

Единственное, что отличается между моим кодом и остальным, это метод getRand (), который, я уверен, просто возвращает число между min и max, которое передается.

Весь мой код:

    //size of the map
var xsize = 0;
var ysize = 0;

var TILESIZE = 8;
var objects = 0;

//define the %chance to generate either a room or a corridor on the map
//BTW, rooms are 1st priority so actually it's enough to just define the chance
//of generating a room
var chanceRoom = 75;
var chanceCorridor = 25;

//map
var dungeon_map = [];

//a list over tile types we're using
var tileUnused = 0;
var tileDirtWall = 1;
var tileDirtFloor = 2;
var tileStoneWall = 3;
var tileCorridor = 4;
var tileDoor = 5;
var tileUpStairs = 6;
var tileDownStairs = 7;

//setting a tile's type
function setCell(x, y, celltype)
{
   dungeon_map[x + xsize * y] = celltype;
}

//returns the type of a tile
function getCell(x, y)
{
   return dungeon_map[x + xsize * y];
}

function getRand(min, max)
{
   return Math.floor(Math.random() * (max - min + 1) + min);
}

function makeCorridor(x, y, length, direction)
{
   var len = getRand(2, length);
   var floor = tileCorridor;
   var dir = 0;

   if (direction > 0 && direction < 4)
      dir = direction;

   var xtemp = 0;
   var ytemp = 0;

   if (x < 0 || x > xsize)
         return false;
   if (y < 0 || y > ysize)
         return false;

   if (dir == 0)
   {
      // north
      xtemp = x;

      //make sure its not out of bounds
      for (ytemp = y; ytemp > (y - len); ytemp--)
      {
         if (ytemp < 0 || ytemp > ysize)
            return false;
         if (getCell(xtemp, ytemp) != tileUnused)
            return false;
      }

      //start building
      for (ytemp = y; ytemp > (y - len); ytemp--)
      {
         setCell(xtemp, ytemp, floor);
      }
   }
   else if (dir == 1)
   {
      // east
      ytemp = y;

      for (xtemp = x; xtemp < (x + len); xtemp++)
      {
         if (xtemp < 0 || xtemp > xsize)
            return false;
         if (getCell(xtemp, ytemp) != tileUnused)
            return false;
      }

      for (xtemp = x; xtemp < (x + len); xtemp++)
      {
         setCell(xtemp, ytemp, floor);
      }
   }
   else if (dir == 2)
   {
      // south
      xtemp = x;

      //make sure its not out of bounds
      for (ytemp = y; ytemp < (y + len); ytemp++)
      {
         if (ytemp < 0 || ytemp > ysize)
            return false;
         if (getCell(xtemp, ytemp) != tileUnused)
            return false;
      }

      //start building
      for (ytemp = y; ytemp < (y + len); ytemp++)
      {
         setCell(xtemp, ytemp, floor);
      }
   }
   else if(dir == 3)
   {
      // west
      ytemp = y;

      for (xtemp = x; xtemp > (x - len); xtemp--)
      {
         if (xtemp < 0 || xtemp > xsize)
            return false;
         if (getCell(xtemp, ytemp) != tileUnused)
            return false;
      }

      for (xtemp = x; xtemp > (x - len); xtemp--)
      {
         setCell(xtemp, ytemp, floor);
      }
   }

   return true;
}

function makeRoom(x, y, xlength, ylength, direction)
{
   console.log("DIRECTION: " + direction);
   //define the dimensions of the room, it should be at least 4x4 tiles 
   //(2x2 for walking on, the rest is walls)
   var xlen = getRand(4, xlength);
   var ylen = getRand(4, ylength);

   //tile type its going to be filled with
   var floor = tileDirtFloor;
   var wall = tileDirtWall;

   var dir = 0;
   if (direction > 0 && direction < 4)
      dir = direction;

   if (dir == 0)
   {
      //north
      //check if there is enough space left for a room
      for (var ytemp = y; ytemp > (y - ylen); ytemp--)
      {
         if (ytemp < 0 || ytemp > ysize) 
            return false;

         for (var xtemp = (x - xlen / 2); xtemp < (x + (xlen + 1) / 2); xtemp++)
         {
            if (xtemp < 0 || xtemp > xsize) 
               return false;
            if (getCell(xtemp, ytemp) != tileUnused) 
               return false;
         }
      }

      //we're still here, build
      for (var ytemp = y; ytemp > (y - ylen); ytemp--)
      {
         for (var xtemp = (x - xlen / 2); xtemp < (x + (xlen + 1) / 2); xtemp++)
         {
            //start with the walls
            if (xtemp == (x - xlen / 2)) 
               setCell(xtemp, ytemp, wall);
            else if (xtemp == (x + (xlen - 1) / 2)) 
               setCell(xtemp, ytemp, wall);
            else if (ytemp == y) 
               setCell(xtemp, ytemp, wall);
            else if (ytemp == (y - ylen + 1)) 
               setCell(xtemp, ytemp, wall);
            else 
               setCell(xtemp, ytemp, floor); //and then fill with the floor
         }
      }
   }
   else if (dir == 1)
   {
      //east
      for (var ytemp = (y - ylen / 2); ytemp < (y + (ylen + 1) / 2); ytemp++)
      {
         if (ytemp < 0 || ytemp > ysize) 
            return false;

         for (var xtemp = x; xtemp < (x + xlen); xtemp++)
         {
            if (xtemp < 0 || xtemp > xsize) 
               return false;
            if (getCell(xtemp, ytemp) != tileUnused) 
               return false;
         }
      }

      for (var ytemp = (y - ylen / 2); ytemp < (y + (ylen + 1) / 2); ytemp++)
      {
         for (var xtemp = x; xtemp < (x + xlen); xtemp++)
         {
            if (xtemp == x) 
               setCell(xtemp, ytemp, wall);
            else if (xtemp == (x + xlen - 1)) 
               setCell(xtemp, ytemp, wall);
            else if (ytemp == (y - ylen / 2)) 
               setCell(xtemp, ytemp, wall);
            else if (ytemp == (y + (ylen - 1) / 2)) 
               setCell(xtemp, ytemp, wall);
            else 
               setCell(xtemp, ytemp, floor);
         }
      }
   }
   else if (dir == 2)
   {
      //south
      for (var ytemp = y; ytemp < (y + ylen); ytemp++)
      {
         if (ytemp < 0 || ytemp > ysize) 
            return false;

         for (var xtemp = (x - xlen / 2); xtemp < (x + (xlen + 1) / 2); xtemp++)
         {
            if (xtemp < 0 || xtemp > xsize) 
               return false;
            if (getCell(xtemp, ytemp) != tileUnused) 
               return false;
         }
      }

      for (var ytemp = y; ytemp < (y + ylen); ytemp++)
      {
         for (var xtemp = (x - xlen / 2); xtemp < (x + (xlen + 1) / 2); xtemp++)
         {
            if (xtemp == (x - xlen / 2)) 
               setCell(xtemp, ytemp, wall);
            else if (xtemp == (x + (xlen - 1) / 2)) 
               setCell(xtemp, ytemp, wall);
            else if (ytemp == y) 
               setCell(xtemp, ytemp, wall);
            else if (ytemp == (y + ylen - 1)) 
               setCell(xtemp, ytemp, wall);
            else setCell(xtemp, ytemp, floor);
         }
      }
   }
   else if (dir == 3)
   {
      //west
      for (var ytemp = (y - ylen / 2); ytemp < (y + (ylen + 1) / 2); ytemp++)
      {
         if (ytemp < 0 || ytemp > ysize) 
            return false;

         for (var xtemp = x; xtemp > (x - xlen); xtemp--)
         {
            if (xtemp < 0 || xtemp > xsize) 
               return false;
            if (getCell(xtemp, ytemp) != tileUnused) 
               return false; 
         }
      }

      for (var ytemp = (y - ylen / 2); ytemp < (y + (ylen + 1) / 2); ytemp++)
      {
         for (var xtemp = x; xtemp > (x - xlen); xtemp--)
         {
            if (xtemp == x) 
               setCell(xtemp, ytemp, wall);
            else if (xtemp == (x - xlen + 1)) 
               setCell(xtemp, ytemp, wall);
            else if (ytemp == (y - ylen / 2)) 
               setCell(xtemp, ytemp, wall);
            else if (ytemp == (y + (ylen - 1) / 2)) 
               setCell(xtemp, ytemp, wall);
            else setCell(xtemp, ytemp, floor);
         }
      }
   }

   return true;
}

//print map to screen
function showDungeon()
{
   for (var y = 0; y < ysize; y++)
   {
      for (var x = 0; x < xsize; x++)
      {
         var cell = getCell(x, y);

         if (cell == tileUnused)
         {
            ctx.fillStyle = "#fff"; //white
         }
         else if (cell == tileDirtWall)
         {
            ctx.fillStyle = "#663300"; //brown
         }
         else if (cell == tileDirtFloor)
         {
            ctx.fillStyle = "#FFFFCC"; //yellow
         }
         else if (cell == tileStoneWall)
         {
            ctx.fillStyle = "#000"; //black
         }
         else if (cell == tileCorridor)
         {
            ctx.fillStyle = "#0033FF"; //dark blue
         }
         else if (cell == tileDoor)
         {
            ctx.fillStyle = "#00CCFF"; //lightblue
         }
         else if (cell == tileUpStairs)
         {
            ctx.fillStyle = "#00FF33"; //green
         }
         else if (cell == tileDownStairs)
         {
            ctx.fillStyle = "#FF0000"; //red
         }

         ctx.fillRect(x * TILESIZE, y * TILESIZE, TILESIZE, TILESIZE);
      }
   }
}

function createDungeon(inx, iny, inobj)
{
   if (inobj < 1) 
      objects = 10;
   else 
      objects = inobj;

   //adjust the size of the map, if it's smaller or bigger than the limits
   if (inx < 3) 
      xsize = 3;
   else 
      xsize = inx;

   if (iny < 3) 
      ysize = 3;
   else 
      ysize = iny;

   console.log("X size of dungeon: \t" + xsize);
   console.log("Y size of dungeon: \t" + ysize);
   console.log("max # of objects: \t" + objects);

   //redefine the map var, so it's adjusted to our new map size
   dungeon_map = new Array(xsize * ysize);

   //start with making the "standard stuff" on the map
   for (var y = 0; y < ysize; y++)
   {
      for (var x = 0; x < xsize; x++)
      {
         //ie, making the borders of unwalkable walls
         if (y == 0) 
            setCell(x, y, tileStoneWall);
         else if (y == ysize - 1) 
            setCell(x, y, tileStoneWall);
         else if (x == 0) 
            setCell(x, y, tileStoneWall);
         else if (x == xsize - 1) 
            setCell(x, y, tileStoneWall);
         else 
            setCell(x, y, tileUnused);
      }
   }

   /*******************************************************************************
   And now the code of the random-map-generation-algorithm begins!
   *******************************************************************************/

   //start with making a room in the middle, which we can start building upon
   makeRoom(xsize / 2, ysize / 2, 8, 6, getRand(0,3));
   console.log("make room\n" + dungeon_map);
   //keep count of the number of "objects" we've made
   var currentFeatures = 1; //+1 for the first room we just made

   for (var countingTries = 0; countingTries < 1000; countingTries++)
   {
      //check if we've reached our quota
      if (currentFeatures == objects){
         break;
      }

      //start with a random wall
      var newx = 0;
      var xmod = 0;
      var newy = 0;
      var ymod = 0;
      var validTile = -1;

      //1000 chances to find a suitable object (room or corridor)..
      for (var testing = 0; testing < 1000; testing++)
      {
         newx = getRand(1, xsize - 1);
         newy = getRand(1, ysize - 1);
         validTile = -1;

         if (getCell(newx, newy) == tileDirtWall || getCell(newx, newy) == tileCorridor)
         {
            //check if we can reach the place
            if (getCell(newx, newy + 1) == tileDirtFloor || getCell(newx, newy + 1) == tileCorridor)
            {
               validTile = 0;
               xmod = 0;
               ymod = -1;
            }
            else if (getCell(newx - 1, newy) == tileDirtFloor || getCell(newx - 1, newy) == tileCorridor)
            {
               validTile = 1;
               xmod = +1;
               ymod = 0;
            }
            else if (getCell(newx, newy - 1) == tileDirtFloor || getCell(newx, newy - 1) == tileCorridor)
            {
               validTile = 2;
               xmod = 0;
               ymod = +1;
            }
            else if (getCell(newx + 1, newy) == tileDirtFloor || getCell(newx + 1, newy) == tileCorridor)
            {
               validTile = 3;
               xmod = -1;
               ymod = 0;
            }

            //check that we haven't got another door nearby, so we won't get alot of openings besides
            //each other
            if (validTile > -1)
            {
               if (getCell(newx, newy + 1) == tileDoor) //north
                  validTile = -1;
               else if (getCell(newx - 1, newy) == tileDoor)//east
                  validTile = -1;
               else if (getCell(newx, newy - 1) == tileDoor)//south
                  validTile = -1;
               else if (getCell(newx + 1, newy) == tileDoor)//west
                  validTile = -1;
            }

            //if we can, jump out of the loop and continue with the rest
            if (validTile > -1) 
               break;
         }
      }

      if (validTile > -1)
      {
         //choose what to build now at our newly found place, and at what direction
         var feature = getRand(0, 100);
         if (feature <= chanceRoom)
         { 
            if (makeRoom((newx + xmod), (newy + ymod), 8, 6, validTile))
            {
               //a new room
               currentFeatures++; //add to our quota
               //then we mark the wall opening with a door
               setCell(newx, newy, tileDoor);
               //clean up infront of the door so we can reach it
               setCell((newx + xmod), (newy + ymod), tileDirtFloor);
            }
         }
         else if (feature >= chanceRoom)
         { //new corridor
            if (makeCorridor((newx + xmod), (newy + ymod), 6, validTile))
            {
               //same thing here, add to the quota and a door
               currentFeatures++;
               setCell(newx, newy, tileDoor);
            }
         }
      }
   }

   console.log("\ndone making room\n" + dungeon_map);
   /*******************************************************************************
   All done with the building, let's finish this one off
   *******************************************************************************/

   //sprinkle out the bonusstuff (stairs, chests etc.) over the map
   var newx = 0;
   var newy = 0;
   var ways = 0; //from how many directions we can reach the random spot from
   var state = 0; //the state the loop is in, start with the stairs

   while (state != 10)
   {
      for (var testing = 0; testing < 1000; testing++)
      {
         newx = getRand(1, xsize - 1);
         newy = getRand(1, ysize - 2); //cheap bugfix, pulls down newy to 0<y<24, from 0<y<25

         ways = 4; //the lower the better

         //check if we can reach the spot
         if (getCell(newx, newy + 1) == tileDirtFloor || getCell(newx, newy + 1) == tileCorridor)
         {
            //north
            if (getCell(newx, newy + 1) != tileDoor)
               ways--;
         }
         if (getCell(newx - 1, newy) == tileDirtFloor || getCell(newx - 1, newy) == tileCorridor)
         {
            //east
            if (getCell(newx - 1, newy) != tileDoor)
               ways--;
         }
         if (getCell(newx, newy - 1) == tileDirtFloor || getCell(newx, newy - 1) == tileCorridor)
         {
            //south
            if (getCell(newx, newy - 1) != tileDoor)
               ways--;
         }
         if (getCell(newx + 1, newy) == tileDirtFloor || getCell(newx + 1, newy) == tileCorridor)
         {
            //west
            if (getCell(newx + 1, newy) != tileDoor)
               ways--;
         }

         //console.log("ways: " + ways);

         if (state == 0)
         {
            if (ways == 0)
            {
               console.log("upstairs");
               //we're in state 0, let's place a "upstairs" thing
               setCell(newx, newy, tileUpStairs);
               state = 1;
               break;
            }
         }
         else if (state == 1)
         {
            if (ways == 0)
            {
               console.log("downstairs");
               //state 1, place a "downstairs"
               setCell(newx, newy, tileDownStairs);
               state = 10;
               break;
            }
         }
      }
   }

   //all done with the map generation, tell the user about it and finish
   console.log("# of objects made: \t" + currentFeatures);

   return true;
}


///////////////////////////////////////////////////////////////////////////////////////////////////
var x = 70;
var y = 70;
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = x*TILESIZE;
canvas.height = y*TILESIZE;
document.body.appendChild(canvas);

var dungeon_objects = 40;

//then we create a new dungeon map
if (createDungeon(x, y, dungeon_objects))
{
   //always good to be able to see the results..
   showDungeon();
}

и jfiddle .

1 Ответ

1 голос
/ 02 сентября 2011

Итак, я разобрался с проблемой.Все переменные были Java-типами, и когда я преобразовал их в javascript, я забыл об этом.Таким образом, в частях кода, где вы делите пополам, переменная получена с десятичными знаками.Поэтому, чтобы исправить это, я просто делал Math.floor () всякий раз, когда делил пополам, а затем подправлял некоторые операторы if, чтобы соответствовать, чтобы исправить стены.

...