2011-09-01 40 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