<div id="app"></app>
html {
  font-family: georgia, serif;
  width: 100%;
  height: 100%;
  background-color: #636363 ;
}
#app{
  background-color: #636363 ;
}
#root {
  background: #636363;
}



.row {
    margin: 0 auto;
    max-width: 1000px;
    width: 100%;
    display: flex;
    right: 0;
    text-align: center;
    flex-wrap: wrap;
}

.left {
    display: inline-block;
    text-align: left;
}

.right {
    display: inline-block;
    text-align: right;
}

.center {
    display: inline-block;
    margin: 0 auto;
}
.col-4 {
    width: 33.33%;
}

input{
  margin-left: 5px;
}

label {
  font-family: "Arial Black";
  font-variant: small-caps;
  font-size: 1.2em;
  color: #333;
}

.grid {
  margin: .5em auto;
  background: linear-gradient(#f7aeb7, #ffcccc, #ffeee6);
}

table {
  border-collapse: collapse;
}

td {
  min-width: 2em;
  width: 2em;
  height: 2em;
  text-align: center;
  bottom-padding: 0 ;

}

.wall:before {
  content: "1";
}

.tunnel:before {
  content: "0";
}

.wall {
  background: #636363;
  bottom-padding: 0 ;
}

.tunnel {
  background: transparent;
}

.form-control {
  width: 60px;
  text-align: center;
}


/* Custom, iPhone Retina */

@media only screen and (min-width: 120px) {
  td {
    min-width: 2em;
    width: 2em;
    height: 2em;
    font-size: .4em;
  }
  label{
    font-size:1em;
  }
}


/* Extra Small Devices, Phones */

@media only screen and (min-width: 480px) {
  td {
    min-width: 2em;
    width: 2em;
    height: 2em;
    font-size: .6em;
  }
    label{
    font-size:.8em;
  }
}

@media only screen and (min-width: 555px) {
    label{
    font-size:1em;
  }
}

/* Small Devices, Tablets */

@media only screen and (min-width: 768px) {
  td {
    min-width: 2em;
    width: 2em;
    height: 2em;
    font-size: 1em;
  }
    label{
    font-size:1.2em;
  }
}


/* Medium Devices, Desktops */

@media only screen and (min-width: 992px) {
  td {
    min-width: 2em;
    width: 2em;
    height: 2em;
    font-size: 1em;
  }
    label{
    font-size:1.2em;
  }
}


/* Large Devices, Wide Screens */

@media only screen and (min-width: 1200px) {
  td {
    min-width: 2em;
    width: 2em;
    height: 2em;
    font-size: 1em;
  }
  label{
    font-size:1.2em;
  }
}
/*
 * A simple React component
 */
class Application extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
      dimensions: 20,
      maxTunnels: 50,
      maxLength: 8
    };
    this.onClick = this.onClick.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  createArray(num, dimensions) {
    var array = [];
    for (var i = 0; i < dimensions; i++) {
      array.push([]);
      for (var j = 0; j < dimensions; j++) {
        array[i].push(num);
      }
    }
    return array;
  }

  onChange(e) {
    this.setState({
      [e.target.name]: this.validator(e.target.value)
    });
  }

  validator(x) {
    let input = Number(x);
    if (isNaN(input)){
      return 0;
    }
    return input;
  }

  //lets create a randomly generated map for our dungeon crawler
  createMap() {
    let dimensions = this.state.dimensions, // width and height of the map
      maxTunnels = this.state.maxTunnels, // max number of tunnels possible
      maxLength = this.state.maxLength, // max length each tunnel can have
      map = this.createArray(1, dimensions), // create a 2d array full of 1's
      currentRow = Math.floor(Math.random() * dimensions), // our current row - start at a random spot
      currentColumn = Math.floor(Math.random() * dimensions), // our current column - start at a random spot
      directions = [[-1, 0], [1, 0], [0, -1], [0, 1]], // array to get a random direction from (left,right,up,down)
      lastDirection = [], // save the last direction we went
      randomDirection; // next turn/direction - holds a value from directions

    // lets create some tunnels - while maxTunnels, dimentions, and maxLength  is greater than 0.
    while (maxTunnels && dimensions && maxLength) {

      // lets get a random direction - until it is a perpendicular to our lastDirection
      // if the last direction = left or right,
      // then our new direction has to be up or down,
      // and vice versa
      do {
         randomDirection = directions[Math.floor(Math.random() * directions.length)];
      } while ((randomDirection[0] === -lastDirection[0] && randomDirection[1] === -lastDirection[1]) || (randomDirection[0] === lastDirection[0] && randomDirection[1] === lastDirection[1]));

      var randomLength = Math.ceil(Math.random() * maxLength), //length the next tunnel will be (max of maxLength)
        tunnelLength = 0; //current length of tunnel being created

		// lets loop until our tunnel is long enough or until we hit an edge
      while (tunnelLength < randomLength) {

        //break the loop if it is going out of the map
        if (((currentRow === 0) && (randomDirection[0] === -1)) ||
            ((currentColumn === 0) && (randomDirection[1] === -1)) ||
            ((currentRow === dimensions - 1) && (randomDirection[0] === 1)) ||
            ((currentColumn === dimensions - 1) && (randomDirection[1] === 1))) {
          break;
        } else {
          map[currentRow][currentColumn] = 0; //set the value of the index in map to 0 (a tunnel, making it one longer)
          currentRow += randomDirection[0]; //add the value from randomDirection to row and col (-1, 0, or 1) to update our location
          currentColumn += randomDirection[1];
          tunnelLength++; //the tunnel is now one longer, so lets increment that variable
        }
      }

      if (tunnelLength) { // update our variables unless our last loop broke before we made any part of a tunnel
        lastDirection = randomDirection; //set lastDirection, so we can remember what way we went
        maxTunnels--; // we created a whole tunnel so lets decrement how many we have left to create
      }
    }
    return map; // all our tunnels have been created and our map is complete, so lets return it to our render()
  };

  onClick(e) {
    this.forceUpdate()
  }
  render() {
    let grid = this.createMap();
    return (
      <div >
        <div className="form-group row">
          <div className="left col-4">
            <label>dimensions</label>
            <input className="form-control" name="dimensions" type="text" maxLength="2" value={this.state.dimensions} onChange={this.onChange}/>
          </div>
          <div className="center col-4">
            <label>maxTunnels</label>
            <input className="form-control" name="maxTunnels" type="text" maxLength="3" value={this.state.maxTunnels} onChange={this.onChange}/>
          </div>
          <div className="right col-4">
            <label>maxLength</label>
            <input className="form-control" name="maxLength" type="text" maxLength="3" value={this.state.maxLength} onChange={this.onChange}/>
          </div>
        </div>
        <table className="grid" onClick={this.onClick}>
          <thead>
            {grid.map((obj, row) => <tr key={row}>{obj.map((obj2, col) =>< td className = {
                obj2 === 1
                  ? 'wall'
                  : 'tunnel'
              }
              key = {
                col
              } > </td>)}</tr>)}
          </thead>
        </table>
      </div>
    );
  }
}

/*
 * Render the above component into the div#app
 */
React.render(<Application />, document.getElementById('app'));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/0.13.0/react.min.js