2011-09-01 46 views
-1

我想将此dungeon algorithm从java转换为javascript,但是,我的脚本在70%的时间内工作。当它工作时,问题是:房间一侧缺少一面墙,部分房间无法以任何方式访问。 当它不起作用时,它会陷入无限循环。将地牢算法从java转换为javascript不工作

room is missing a side

room is missing a side

room is missing a side

(对不起都小,我刚刚更新的图片我jfiddle和输出较大http://jsfiddle.net/gUmH7/1/

我猜makeRoom()是问题,如果不是,它的definitly createDungeon()。 所以当算法运行时,在第一次调用makeRoom()之后,我得到了一些1和2,在我的dungeon_map数组中,其中1是棕色墙,2是黄色地板。算法不起作用时,dungeon_map数组中没有任何1或2,导致无限循环。

我很确定java代码的工作原理,因为here is one with the output online。这里是original

我的代码与其他人之间的区别,唯一的事情是getRand()方法,我敢肯定,只是返回传入的最小值和最大值之间的数字

我的全部代码:

//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

+3

有没有什么办法可以尝试减少到​​小测试用例? – templatetypedef

+5

_“我猜测makeRoom()是问题,如果不是的话,它的definitly createDungeon()。”_ - 所以你说你已经缩小到一个150行功能或另一个150行功能?那么,只要我知道在哪里看... – nnnnnn

+0

getRandom似乎工作http://jsfiddle.net/mplungjan/Z522m/ – mplungjan

回答

1

所以我想出了问题。所有的变量都是Java ints,当我转换为javascript时,我忘记了这一点。因此,在您将代码分成一半的部分中,变量带有小数。因此,为了解决这个问题,我只是将Math.floor()分成了两半,然后我调整了一些if语句来匹配墙壁。

+0

而不是Math.floor(),请尝试右移(x >> 1)。 – Ron

+0

@Ron将结果除以2.我认为你的意思是'x >> 0'或'x << 0'。 – Phrogz

+0

@Progrog我的意思是代替Math.floor(x/2),尝试(x >> 1)。 – Ron