diff --git a/nurikabe.coffee b/nurikabe.coffee new file mode 100644 index 0000000..48d903a --- /dev/null +++ b/nurikabe.coffee @@ -0,0 +1,153 @@ +window.Nurikabe = class Nurikabe + constructor: (@board) -> + @grid = document.createElement 'div' + @grid.classList.add 'game-container' + @board.appendChild @grid + + buttons_div = document.createElement 'div' + buttons = {check: 'Check', reset: 'Reset', newgame: 'New game', help: '?'} + + for k,v of buttons + button = document.createElement 'button' + button.innerHTML = v + button.classList.add k + + buttons_div.appendChild button + + @board.appendChild buttons_div + + @board.querySelector('.check').addEventListener('click', @check.bind(this)) + @board.querySelector('.reset').addEventListener('click', @reset.bind(this)) + + check: -> + errors = @errors() + + if errors.length == 0 + alert 'Congratulations!' + else + alert errors.map((el) -> el.message).join() + + errors: -> + solution = @toArray() + errors = [] + processed_cells = [] + black_stream = new Stream(solution) + white_walls = [] + + for i in [0...solution.length] + row = solution[i] + for j in [0...row.length] + cell = solution[i][j] + + if cell < 0 + if black_stream.empty() + black_stream.calculate({x: i, y: j}) + else if !black_stream.include({x: i, y: j}) + errors.push {row: i, column: j, message: 'The stream must be continuous'} + else if cell > 0 + if white_walls.some((wall) -> wall.include({x: i, y: j})) + errors.push {row: i, column: j, message: 'Each wall must contain exactly one numbered cell.'} + else + wall = new Stream(solution) + wall.calculate({x: i, y: j}) + + if wall.length() != cell + errors.push {row: i, column: j, message: 'Each numbered cell is a wall cell, the number in it is the number of cells in that wall.'} + + white_walls.push(wall) + + errors + + generate: (game, solution = false) -> + @game = game if game? + @grid.innerHTML = @game.map((row) -> + '
' + row.map((cell) -> + if cell <= 0 + color_class = 'black' if solution && cell == -1 + "
 
" + else + "
#{cell}
" + ).join('') + '
' + ).join('') + + for cell in board.querySelectorAll('.empty') + cell.addEventListener 'click', ((evenment) => @toggle evenment.target), false + + return + + reset: -> + @generate() + + toggle: (cell) -> + if cell.classList.contains 'black' + cell.classList.remove 'black' + cell.classList.add 'white' + else if cell.classList.contains 'white' + cell.classList.remove 'white' + else + cell.classList.add 'black' + + toArray: -> + [].map.call @grid.querySelectorAll('.grid-row'), (row) -> + [].map.call row.querySelectorAll('.grid-cell'), (cell) -> + if cell.classList.contains('empty') + if cell.classList.contains('black') + -1 + else + 0 + else + parseInt(cell.innerHTML) + +class Stream + constructor: (@game) -> + @cells = [] + + calculate: (cell) -> + value = @game[cell.x][cell.y] + @cells = [] + @type = if value < 0 then 'black' else 'white' + + cell = {x: cell.x, y: cell.y, value: value} + cells_to_process = [cell] + + while cells_to_process.length > 0 + cell = cells_to_process.pop() + cells_to_process = cells_to_process.concat @process(cell) unless @include(cell) + + + @cells + + checkCell: (cell, value) -> + 0 <= cell.x < @game.length && 0 <= cell.y < @game[cell.x].length && + (value < 0 && @game[cell.x][cell.y] < 0 || value >= 0 && @game[cell.x][cell.y] >= 0) + + empty: -> + @cells.length == 0 + + getCell: (cell, value) -> + {x: cell.x, y: cell.y, value: @game[cell.x][cell.y]} if @checkCell(cell, value) + + include: (cell) -> + @cells.indexOf("#{cell.x};#{cell.y}") >= 0 + + length: -> + @cells.length + + process: (cell) -> + @cells.push("#{cell.x};#{cell.y}") + + x = cell.x + y = cell.y + value = cell.value + + cells_to_add = [] + tmp_cell = @getCell({x: x+1, y: y}, value) + cells_to_add.push tmp_cell if tmp_cell? + tmp_cell = @getCell({x: x-1, y: y}, value) + cells_to_add.push tmp_cell if tmp_cell? + tmp_cell = @getCell({x: x, y: y+1}, value) + cells_to_add.push tmp_cell if tmp_cell? + tmp_cell = @getCell({x: x, y: y-1}, value) + cells_to_add.push tmp_cell if tmp_cell? + + cells_to_add diff --git a/nurikabe.html b/nurikabe.html new file mode 100644 index 0000000..7389c8d --- /dev/null +++ b/nurikabe.html @@ -0,0 +1,69 @@ + + + + Nurikabe + + + + + + + +

Nurikabe

+ +
+
+ +