[Picross-commit] SF.net SVN: picross:[137] trunk
Status: Pre-Alpha
Brought to you by:
yvan_norsa
From: <yva...@us...> - 2020-03-14 09:39:38
|
Revision: 137 http://sourceforge.net/p/picross/code/137 Author: yvan_norsa Date: 2020-03-14 09:39:37 +0000 (Sat, 14 Mar 2020) Log Message: ----------- added JS phaser implementation Added Paths: ----------- trunk/phaser/ trunk/phaser/Box.js trunk/phaser/CompletedHints.js trunk/phaser/GameScene.js trunk/phaser/GridAction.js trunk/phaser/GridController.js trunk/phaser/GridMediator.js trunk/phaser/GridModel.js trunk/phaser/GridUI.js trunk/phaser/HintBox.js trunk/phaser/MainMenu.js trunk/phaser/RandomPicrossModel.js trunk/phaser/SimpleButton.js trunk/phaser/UIBox.js trunk/phaser/index.html trunk/phaser/picross.js Added: trunk/phaser/Box.js =================================================================== --- trunk/phaser/Box.js (rev 0) +++ trunk/phaser/Box.js 2020-03-14 09:39:37 UTC (rev 137) @@ -0,0 +1,110 @@ +/* Copyright Yvan Norsa (2007-2020) + * + * yva...@gm... + * + * This software is a computer program whose purpose is to [describe + * functionalities and technical features of your software]. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + + /** Possibles states of a box. */ + class BoxState { }; + /** An empty box. */ + BoxState.EMPTY = 0; + + /** A checked box. */ + BoxState.CHECKED = 1; + + /** A crossed box. */ + BoxState.CROSSED = 2; + +/** + * Representation of a box in the grid. + * + * @author Y. Norsa + */ + class Box { + + /*** Constructor ***/ + + /** Constructor. */ + constructor() { + this.empty(); + this.hash = Math.random(); + } + + + hashCode() { + return this.hash; + } + + /*** Methods ***/ + + /** + * Permits to know if the box is checked. + * + * @return boolean telling if the box is checked + */ + isChecked() { + return this.state == BoxState.CHECKED; + } + + /** + * Permits to know if the box is crossed. + * + * @return boolean telling if the box is crossed + */ + isCrossed() { + return this.state == BoxState.CROSSED; + } + + /** + * Permits to know if the box is empty. + * + * @return boolean telling if the box is empty + */ + isEmpty() { + return this.state == BoxState.EMPTY; + } + + /** Empties the box. */ + empty() { + this.state = BoxState.EMPTY; + } + + /** Checks the box. */ + check() { + this.state = BoxState.CHECKED; + } + + /** Crosses the box. */ + cross() { + this.state = BoxState.CROSSED; + } +} + Added: trunk/phaser/CompletedHints.js =================================================================== --- trunk/phaser/CompletedHints.js (rev 0) +++ trunk/phaser/CompletedHints.js 2020-03-14 09:39:37 UTC (rev 137) @@ -0,0 +1,193 @@ +/* Copyright Yvan Norsa (2007-2020) + * + * yva...@gm... + * + * This software is a computer program whose purpose is to [describe + * functionalities and technical features of your software]. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + +/** + * This class contains the list of hints, marking those which are completed. + * + * @author Y. Norsa + */ +class CompletedHints { + /*** Constructor ***/ + + /** + * Constructor. + * + * @param colsHintsWidth width of the column hints list + * @param colsHintsHeight height of the column hints list + * @param rowsHintsWidth width of the row hints list + * @param rowsHintsHeight height of the row hints list + * @throws IllegalArgumentException if one of the parameters is <= 0 + */ + constructor( colsHintsWidth, colsHintsHeight, + rowsHintsWidth, rowsHintsHeight) + { + + /** List of completed column hints. */ + this.completedCols = Array(colsHintsWidth).fill().map(() => Array(colsHintsHeight).fill(false)); + /** List of completed row hints. */ + this.completedRows = Array(rowsHintsWidth).fill().map(() => Array(rowsHintsHeight).fill(false)); + + } + + /*** Methods ***/ + + /** + * Tells wether a specific column hint is complete. + * + * @param x column number + * @param y hint position + * @return boolean telling wether the hint is completed + */ + isColHintComplete( x, y) { + return this.completedCols[x][y]; + } + + /** + * Returns the list of the completed column hints. + * + * @param column column number + * @return list of completed hints in the column + * @throws IllegalArgumentException if <code>column</code> is lesser + * than 0 or greater than the number of hints columns + */ + getCompleteColHints( column) { + + + var res = []; + + for (var i = 0; i < this.completedCols[column].length; i++) { + if (this.completedCols[column][i]) { + res.push(i); + } + } + + return res; + } + + /** + * Resets the state of a hint. + * + * @param col column number + * @param hintIndex hint position + */ + clearColHint( col, hintIndex) { + this.completedCols[col][hintIndex] = false; + } + + /** + * Marks a hint as complete. + * + * @param col column number + * @param index hint position + */ + setCompleteColHint( col, index) { + this.completedCols[col][index] = true; + } + + /** + * Marks a whole column as complete. + * + * @param col column number + */ + setCompleteCol( col) { + for (var i = 0; i < this.completedCols[col].length; i++) { + this.setCompleteColHint(col, i); + } + } + + /** + * Tells wether a specific row hint is complete. + * + * @param x row number + * @param y hint position + * @return boolean telling wether the hint is completed + */ + isRowHintComplete( x, y) { + return this.completedRows[x][y]; + } + + /** + * Returns the list of the completed row hints. + * + * @param row row number + * @return list of completed hints in the row + * @throws IllegalArgumentException if <code>row</code> is lesser + * than 0 or greater than the number of hints row + */ + getCompleteRowHints( row) { + + + var res = []; + + for (var i = 0; i < this.completedRows[row].length; i++) { + if (this.completedRows[row][i]) { + res.push(i); + } + } + + return res; + } + + /** + * Resets the state of a hint. + * + * @param row row number + * @param hintIndex hint position + */ + clearRowHint( row, hintIndex) { + this.completedRows[row][hintIndex] = false; + } + + /** + * Marks a hint as complete. + * + * @param row row number + * @param index hint position + */ + setCompleteRowHint( row, index) { + this.completedRows[row][index] = true; + } + + /** + * Marks a whole row as complete. + * + * @param row row number + */ + setCompleteRow( row) { + for (var i = 0; i < this.completedRows[row].length; i++) { + this.setCompleteRowHint(row, i); + } + } +} + Added: trunk/phaser/GameScene.js =================================================================== --- trunk/phaser/GameScene.js (rev 0) +++ trunk/phaser/GameScene.js 2020-03-14 09:39:37 UTC (rev 137) @@ -0,0 +1,119 @@ +/* Copyright Yvan Norsa (2007-2020) + * + * yva...@gm... + * + * This software is a computer program whose purpose is to [describe + * functionalities and technical features of your software]. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + +class GameScene extends Phaser.Scene { + constructor () { + super({ key: 'game' }); + } + + preload () { + this.load.setBaseURL('http://picross.sourceforge.net/phaser/images/'); + this.load.image('checked-rollover', 'checked-rollover.png'); + this.load.image('checked', 'checked.png'); + this.load.image('crossed', 'crossed.png'); + this.load.image('crossed-rollover', 'crossed-rollover.png'); + this.load.image('empty-rollover', 'empty-rollover.png'); + this.load.image('empty', 'empty.png'); + this.load.image('hint', 'hint.png'); + } + + + create () { + //this.stage.backgroundColor = "#FFFFFF"; + +/* + + if (this.view.isInGrid(point)) { + int row = this.view.getRow(point); + int column = this.view.getColumn(point); + + this.view.setRollover(row, column); + } else { + this.view.rolloverEnded(); + } + */ + +/* + // Paints the boxes + for (var i = 0; i < 200; i=i+25) { + for (var j = 0; j < 300; j=j+25) { + this.add.image(i, j, "empty").setOrigin(0, 0); + } + } + */ + + var gridModel = new RandomPicrossModel(); + + var width = gridModel.width; + var height = gridModel.height; + + this.model = new GridMediator(width, height, gridModel.data, this); + + + //this.input.on('pointermove', function (pointer) {this.model.controller.mouseMoved(pointer)}, this); + this.input.on('pointermove', this.model.controller.mouseMoved, this.model.controller); + this.input.on('pointerdown', this.model.controller.mousePressed, this.model.controller); + this.input.on('pointerup', this.model.controller.mouseReleased, this.model.controller); + + this.model.view.paintComponent(this); + +/* +this.add.line(0, 0, 200, 200, 300, 200, 0xffaabb); + + var rect = new Phaser.GameObjects.Rectangle(this, 0,15 + + (2 * GridUI.BOX_HEIGHT), + 15, + GridUI.BOX_HEIGHT,).setOrigin(0); + rect.setFillStyle(0xff0000); + this.children.add(rect); +*/ + + + + } + + update() { + /* + for (var i = 0; i < 200; i=i+25) { + for (var j = 0; j < 300; j=j+25) { + if ((this.rolloverRow * 25) == j && (this.rolloverColumn * 25) == i) { + this.add.image(i, j, "empty-rollover").setOrigin(0, 0); + } + } + } + */ + + this.model.view.repaint(this); + } +}; \ No newline at end of file Added: trunk/phaser/GridAction.js =================================================================== --- trunk/phaser/GridAction.js (rev 0) +++ trunk/phaser/GridAction.js 2020-03-14 09:39:37 UTC (rev 137) @@ -0,0 +1,48 @@ +/* Copyright Yvan Norsa (2007-2020) + * + * yva...@gm... + * + * This software is a computer program whose purpose is to [describe + * functionalities and technical features of your software]. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + + class GridAction { } + /** Unrecognized action. */ + GridAction.UNKNOWN = 0; + + /** Checking a box. */ + GridAction.CHECK = 1; + + /** Crossing a box. */ + GridAction.CROSS = 2; + + /** Erasing a box. */ + GridAction.EMPTY = 3; + + Added: trunk/phaser/GridController.js =================================================================== --- trunk/phaser/GridController.js (rev 0) +++ trunk/phaser/GridController.js 2020-03-14 09:39:37 UTC (rev 137) @@ -0,0 +1,254 @@ +/* Copyright Yvan Norsa (2007-2020) + * + * yva...@gm... + * + * This software is a computer program whose purpose is to [describe + * functionalities and technical features of your software]. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + + class GridController { + + /** The view to which the controller is attached. */ + //private GridUI view = null; + + /** Indicates wether the erase mode is "on" or not. */ + //private boolean eraseMode; + + /*** Method overloaded from the class Controller ***/ + + constructor(mediator) { + this.mediator = mediator; + } + + doPaintCommand(row, col) { + + this.view.repaint(row, col); + + } + + + doRepaintTopHintsCommand(col) { + this.view.repaintColHints(col); + } + + doRepaintLeftHintsCommand(row) { + this.view.repaintRowHints(row); + } + + doEraseModeCommand() { + this.eraseMode = true; + } + + doGridFilledCommand() { + this.view.rolloverEnded(); + this.view.disableGrid(); + + } + + eventPerformed( e) { + /* + String cmd = e.getCommandName(); + + if (cmd.equals(GridCommands.GRID_FILLED_CMD)) { + this.view.rolloverEnded(); + this.view.disableGrid(); + + return; + } + + if (cmd.equals(GridCommands.PAINT_CMD)) { + PaintCommand command = (PaintCommand) e.getCommand(); + this.view.repaint(command.getRow(), command.getColumn()); + + return; + } + + if (cmd.equals(GridCommands.REPAINT_TOP_HINTS_CMD)) { + int col = ((RepaintTopHintsCommand) e.getCommand()).getColumn(); + this.view.repaintColHints(col); + + return; + } + + if (cmd.equals(GridCommands.REPAINT_LEFT_HINTS_CMD)) { + int row = ((RepaintLeftHintsCommand) e.getCommand()).getRow(); + this.view.repaintRowHints(row); + + return; + } + + if (cmd.equals(GridCommands.ERASE_MODE_CMD)) { + this.eraseMode = true; + return; + } + + if (cmd.equals(GridCommands.REPAINT_EVERYTHING_CMD)) { + this.view.doRepaint(); + return; + } + */ + } + + /*** Methods implanted from the interface MouseListener ***/ + mouseClicked( e) { } + +mouseEntered( e) { } + + +mouseExited( e) { + this.view.rolloverEnded(); + this.view.highlightEnded(); + } + + + mousePressed (e) { + //this.view.rolloverEnded(); + } + +mouseReleased( e) { + this.checkAndFill(e); + this.eraseMode = false; + this.mediator.doEndActionCommand(); + } + + /*** Methods implanted from the interface MouseMotionListener ***/ + /* + mouseDragged (e) { + this.checkAndFill(e); + } +*/ + + mouseMoved( e) { + // dragging + + /* + if (e.leftButtonDown() || e.rightButtonDown()) { + + this.checkAndFill(e); + + + + } else { +*/ + var point = e.position; + + if (this.view.isInGrid(point)) { + var row = this.view.getRow(point); + var column = this.view.getColumn(point); + + this.view.setRollover(row, column); + +/**/ + if (e.leftButtonDown() || e.rightButtonDown()) { + + this.checkAndFill(e); + } +/**/ + + + } else { + this.view.rolloverEnded(); + } + // } + } + + /*** Methods ***/ + + /** + * Checks if the mouse current click's location is inside the grid + * and eventually fills the corresponding box. + * + * @param e mouse event to handle + */ + checkAndFill( e) { +/* var modifiers = e.getModifiers(); + + if (modifiers != MouseEvent.BUTTON1_MASK + && modifiers != MouseEvent.BUTTON3_MASK) { +*/ +if (!e.leftButtonDown() && !e.leftButtonReleased() + && !e.rightButtonDown() && !e.rightButtonReleased()) { + return; + } + + var point = e.position; + + if (this.view.isInGrid(point)) { + var row = this.view.getRow(point); + var column = this.view.getColumn(point); + + var type; + + if (this.eraseMode) { + type = GridAction.EMPTY; + } else { + type = GridController.modifiersToType(e.event); + } + + this.view.rolloverHighlight(row, column); + + //this.fireEventPerformed(GridCommands.FILL_CMD, + this.mediator.doFillCommand(row, column, type); + } else { + this.view.highlightEnded(); + } + } + + /** + * Converts a mouse click to an action. + * + * @param modifiers mouse event modifiers + * @return corresponding action, or -1 + */ + static modifiersToType( e) { + // Chrome fix + if (e.button === 2 || e.buttons === 2) { + return GridAction.CROSS; + } + + if (e.button === 0) { + return GridAction.CHECK; + } + + return GridAction.UNKNOWN; + + } + + /*** Accessor ***/ + + /** + * Allows to set the view. + * + * @param view view to which this controller is attached + */ + setView( view) { + this.view = view; + } +} + Added: trunk/phaser/GridMediator.js =================================================================== --- trunk/phaser/GridMediator.js (rev 0) +++ trunk/phaser/GridMediator.js 2020-03-14 09:39:37 UTC (rev 137) @@ -0,0 +1,114 @@ +/* Copyright Yvan Norsa (2007-2020) + * + * yva...@gm... + * + * This software is a computer program whose purpose is to [describe + * functionalities and technical features of your software]. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + +class GridMediator { + constructor(width, height, data, scene) { + this.model = new GridModel(data); + + this.controller = this.initController(); + //controller.addSimpleListener(this); + //this.addSimpleListener(controller); + + var tmpBoxes = this.model.boxes; + var boxes = []; + + for (var i = 0; i < tmpBoxes.length; i++) { + boxes[i] = []; + + for (var j = 0; j < tmpBoxes[i].length; j++) { + boxes[i][j] = UIBox.fromBox(tmpBoxes[i][j]); + } + } + + var hints = this.model.getCompletedHints(); + + var colData = this.model.colData; + var rowData = this.model.rowData; + + //this.initGrid(width, height, boxes, colData, rowData, hints, controller); + this.view = new GridUI(width, height, + boxes, + colData, + rowData, + hints, + null); + + this.controller.setView(this.view); + + this.view.init(scene); + + } + + + initController() { + return new GridController(this); + } + + + + doFillCommand(row, col, type) { + + this.model.actOnBox(row, col, type); + + // TODO what now ? check the model'state + this.view.updateBox(col, row, this.model.getBoxes()[col][row]); + + this.controller.doPaintCommand(row, col); + + this.controller.doRepaintLeftHintsCommand(row); + this.controller.doRepaintTopHintsCommand(col); + + if (this.model.checkCompleted()) { + this.congratulations(); + } else { + if (this.model.getEraseMode()) { + this.controller.doEraseModeCommand(); + } + } + + return; + } + + doEndActionCommand() { + this.model.endAction(); + } + + congratulations() { + //this.fireEventPerformed(PicrossController.MESSAGE_CMD, + // BundleHelper.getString(this, "victory")); + alert("Congratulations"); + + this.controller.doGridFilledCommand(); + } +} \ No newline at end of file Added: trunk/phaser/GridModel.js =================================================================== --- trunk/phaser/GridModel.js (rev 0) +++ trunk/phaser/GridModel.js 2020-03-14 09:39:37 UTC (rev 137) @@ -0,0 +1,1017 @@ +/* Copyright Yvan Norsa (2007-2020) + * + * yva...@gm... + * + * This software is a computer program whose purpose is to [describe + * functionalities and technical features of your software]. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ + +class GridModel { + constructor(cData) { + this.data = cData; + + var lineSize = this.data[0].length; + + this.boxes = []; + + for (var i = 0; i < this.data.length; i++) { + this.boxes[i] = []; + for (var j = 0; j < lineSize; j++) { + this.boxes[i][j] = new Box(); + } + } + + this.colData = GridModel.getColHints(this.data); + this.rowData = GridModel.getRowHints(this.data); + + this.completedHints = + new CompletedHints(this.data.length, this.colData[0].length, + this.data[0].length, this.rowData[0].length); + + for (var i = 0; i < this.rowData.length; i++) { + if (this.emptyRow(i)) { + this.completedHints.setCompleteRow(i); + } else { + for (var j = 0; j < this.rowData[i].length; j++) { + if (this.rowData[i][j] == GridModel.EMPTY_HINT) { + this.completedHints.setCompleteRowHint(i, j); + } + } + } + } + + for (var i = 0; i < this.colData.length; i++) { + if (this.emptyCol(i)) { + this.completedHints.setCompleteCol(i); + } else { + for (var j = 0; j < this.colData[i].length; j++) { + if (this.colData[i][j] == GridModel.EMPTY_HINT) { + this.completedHints.setCompleteColHint(i, j); + } + } + } + } + + + + + this.rowHintsTotal = GridModel.computeRowHintsTotal(cData); + this.colHintsTotal = GridModel.computeColHintsTotal(cData); + + } + + + /** + * Extracts the column hints from the grid data. + * + * @param data the grid data + * @return column hints + */ + + static getColHints(data) { + // Grid of columns hints + var colHints = []; + + // Largest number of hints for a column + var max = 0; + + for (var i = 0; i < data.length; i++) { + var col = data[i]; + var current = []; + + // Current hint + var chain = 0; + + for (var j = 0; j < col.length; j++) { + var cell = col[j]; + + if (cell) { + chain++; + } else if (chain > 0) { + // We've reached the end of a series of checked boxes + current.push(chain); + chain = 0; + } + } + + if (chain > 0) { + current.push(chain); + } else if (current.length == 0) { + // If this column is empty, we add a "0" hint + current.push(0); + } + + var currentSize = current.length; + + if (currentSize > max) { + max = currentSize; + } + + colHints.push(current); + } + + /* + * So for the hints : + * + * 1 + * 0 2 1 + * + * Which corresponds to the grid : + * + * |-----| + * |_|X|_| + * |_|_|X| + * |_|X|_| + * |_|X|_| + * |-----| + * + * We build this array : + * {{0, -1}, {2, 1}, {1, -1}} + */ + + //int[][] result = new int[data.length][max]; + //var result = new Array(data.length).fill(new Array(max).fill(0)); + var result = []; + + for (var i = 0; i < data.length; i++) { + result[i] = []; + } + + for (var i = 0; i < max; i++) { + // Minimal number of hints for the current column to be considered + var ref = max - i; + + // Current hint row + var currentRow = ref - 1; + + for (var j = 0; j < colHints.length; j++) { + var currentCol = colHints[j]; + var size = currentCol.length; + + if (size >= ref) { + result[j][currentRow] = currentCol[size - ref]; + } else { + result[j][currentRow] = GridModel.EMPTY_HINT; + } + } + } + + return result; + } + + /** + * Extracts the row hints from the grid data. + * + * @param data the grid data + * @return row hints + */ + + static getRowHints( data) { + var rowHints = []; + var max = 0; + + for (var i = 0; i < data[0].length; i++) { + var current = []; + var chain = 0; + + for (var j = 0; j < data.length; j++) { + if (data[j][i]) { + chain++; + } else if (chain > 0) { + current.push(chain); + chain = 0; + } + } + + if (chain > 0) { + current.push(chain); + } else if (current.length == 0) { + current.push(0); + } + + var currentSize = current.length; + + if (currentSize > max) { + max = currentSize; + } + + rowHints.push(current); + } + + //int[][] result = new int[data[0].length][max]; + //var result = new Array(data[0].length).fill(new Array(max).fill(0)); + var result = []; + + for (var i = 0; i < data[0].length; i++) { + result[i] = []; + } + + var nbRows = rowHints.length; + + for (var i = 0; i < max; i++) { + var ref = max - i; + + for (var j = 0; j < nbRows; j++) { + var currentRow = rowHints[j]; + var size = currentRow.length; + + if (size >= ref) { + result[j][i] = currentRow[i - Math.abs(size - max)]; + } else { + result[j][i] = GridModel.EMPTY_HINT; + } + } + } + + return result; + } + + /** + * Returns the index of the first non-empty hint. + * + * @param hints hints array + * @return index of the first non-empty hint + */ + static getFirstHintIndex( hints) { + return GridModel.getNextHintIndex(0, hints); + } + + /** + * Returns the index of the next non-empty hint. + * + * @param currentIndex start index + * @param hints hints array + * @return index of the next non-empty hint + */ + static getNextHintIndex( currentIndex, hints) { + for (var i = currentIndex; i < hints.length; i++) { + if (hints[i] != GridModel.EMPTY_HINT) { + return i; + } + } + + return GridModel.EMPTY_HINT; + } + + /** + * Returns the index of the last non-empty hint. + * + * @param hints hints array + * @return index of the last non-empty hint + */ + static getLastHintIndex( hints) { + return GridModel.getPreviousHintIndex(hints.length - 1, hints); + } + + /** + * Returns the index of the previous non-empty hint. + * + * @param currentIndex start index + * @param hints hints array + * @return index of the previous non-empty hint + */ + static getPreviousHintIndex( currentIndex, hints) { + for (var i = currentIndex; i >= 0; i--) { + if (hints[i] != GridModel.EMPTY_HINT) { + return i; + } + } + + return GridModel.EMPTY_HINT; + } + + static computeRowHintsTotal( data) { + //int[] result = new int[data[0].length]; + var result = new Array(data[0].length); + + for (var i = 0; i < data[0].length; i++) { + var total = 0; + + for (var j = 0; j < data.length; j++) { + if (data[j][i]) { + total++; + } + } + + result[i] = total; + } + + return result; + } + + static computeColHintsTotal( data) { + //int[] result = new int[data.length]; + var result = new Array(data.length); + + for (var i = 0; i < data.length; i++) { + var total = 0; + + //for (boolean cell : data[i]) { + for (var j = 0; j < data[i].length; j++) { + var cell = data[i][j]; + + if (cell) { + total++; + } + } + + result[i] = total; + } + + return result; + } + + /*** Methods ***/ + + /** + * Method called during an action. Does the action on the specified box, + * then checks for completed hints. + * + * @param row row of the box + * @param column column of the box + * @throws IllegalArgumentException if <code>row</code> or + * <code>column</code> is less than 0 + */ + actOnBox( row, column, type) { + + if (type == GridAction.UNKNOWN) { + return; + } + +/* + + console.log("actOnBox(" + row + ", " + column + ")"); + console.log("lastModified == null : " + + (this.lastModified == null)); +*/ +/* + +if (column >= this.boxes.length || row >= this.boxes[0].length) { + console.log("#############"); + debugger; +} +*/ + + //GridModel.log.debug("checkBox(" + row + ", " + column + ")"); + //GridModel.log.debug("lastModified == null : " + // + (lastModified == null)); + + /* + * If we are trying to check the last box we just checked + * (while dragging), do nothing + */ + if (this.lastModified != null + && this.lastModified == this.boxes[column][row]) { + + return; + } + + if (this.lastModified != null) { + /* + * If we are in a box which is in the same state as our aim + * (if we are in a checks serie and we are on a box which + * is already checked), do nothing + */ + + if (this.boxes[column][row] === this.lastModified) { + return; + } + } + + if (!this.boxes[column][row].isEmpty() && type == GridAction.EMPTY) { + this.boxes[column][row].empty(); + + // FIXME + //this.mediator.repaint(row, column); + } else { + + if (this.boxes[column][row].isEmpty() + && (this.lastModified == null || !this.lastModified.isEmpty())) { + + if (type == GridAction.CHECK) { + console.log("checked 1"); + this.boxes[column][row].check(); + } else { //if (type == GridAction.CROSS) { + console.log("crossed 1"); + this.boxes[column][row].cross(); + } + + // FIXME + //this.mediator.repaint(row, column); + } else if (!this.boxes[column][row].isEmpty() + && (this.lastModified == null + || this.lastModified.isEmpty())) { + + if (this.boxes[column][row].isChecked()) { + console.log("checked 2"); + + if (type == GridAction.CHECK) { + this.boxes[column][row].empty(); + this.setEraseMode(); + } else { //if (type == GridAction.CROSS) { + console.log("crossed 2"); + this.boxes[column][row].cross(); + } + } else { //if (this.boxes[column][row].isCrossed()) { + console.log("crossed 3"); + + if (type == GridAction.CROSS) { + this.boxes[column][row].empty(); + this.setEraseMode(); + } else { //if (type == GridAction.CHECK) { + console.log("checked 3"); + + this.boxes[column][row].check(); + } + } + + // FIXME + //this.mediator.repaint(row, column); + } + } + + this.lastModified = this.boxes[column][row]; + this.checkColumn(column); + this.checkRow(row); + //this.checkCompleted(); + } + + //private boolean erase; + + /** Enables the erase mode. */ + setEraseMode() { + // FIXME + //this.mediator.setEraseMode(); + this.erase = true; + } + + getEraseMode() { + var result = this.erase; + this.erase = false; + return result; + } + + /** + * Checks if a row is empty. + * + * @param row row number + * @return boolean stating wether the row is empty or not + */ + emptyRow( row) { + var index = GridModel.getFirstHintIndex(this.rowData[row]); + + return (index == GridModel.getLastHintIndex(this.rowData[row])) + && (this.rowData[row][index] == 0); + } + + /** + * Checks if a hint has been completed in a row. + * + * @param row row number to check + */ + checkRow( row) { + if (this.emptyRow(row)) { + this.completedHints.setCompleteRow(row); + return; + } + + // Contains the completed hints + var completedRowHints = []; + + var leftToRightHints = []; + + // If the first box is empty, skip left-to-right check + if (!this.boxes[0][row].isEmpty()) { + leftToRightHints = this.checkLeftToRight(row); + console.log("left-to-right for row #" + row + ": " + + leftToRightHints); + completedRowHints.push.apply(completedRowHints, leftToRightHints); + } + + var rightToLeftHints = []; + + // Same thing from right to left + if (!this.boxes[this.boxes.length - 1][row].isEmpty()) { + rightToLeftHints = this.checkRightToLeft(row); + //GridModel.log.debug("right-to-left hints for row #" + row + ": " + // + rightToLeftHints); + completedRowHints.push.apply(completedRowHints, rightToLeftHints); + } + + var rowHints = this.completedHints.getCompleteRowHints(row); + + //for (int hintIndex : rowHints) { + for (var i = 0; i < rowHints.length; i++) { + var hintIndex = rowHints[i]; + + if (!this.arrayContains(completedRowHints, hintIndex)) { + this.completedHints.clearRowHint(row, hintIndex); + } + } + + //for (int index : completedRowHints) { + for (var i = 0; i < completedRowHints.length; i++) { + var index = completedRowHints[i]; + this.completedHints.setCompleteRowHint(row, index); + } + + /**/ + var complete = true; + + for (var i = 0; i < this.rowData[row].length; i++) { + if (this.rowData[row][i] != GridModel.EMPTY_HINT && !this.arrayContains(completedRowHints, i)) { + complete = false; + } + } + + if (!complete) { + return; + } + /**/ + + //GridModel.log.debug("Row #" + row + " is complete"); + + // Let's compare number of checked boxes and the sum of all hints + var totalChecked = 0; + + for (var i = 0; i < this.boxes.length; i++) { + if (this.boxes[i][row].isChecked()) { + totalChecked++; + } + } + + /* + * If it's the same number, then we're good to go + * Otherwise, we're gonna keep the highest number of cleared hints + */ + if (totalChecked == this.rowHintsTotal[row]) { + return; + } + + //GridModel.log.debug("totalChecked = " + totalChecked); + //GridModel.log.debug("rowHintsTotal[" + row + "] = " + // + this.rowHintsTotal[row]); + + var leftToRightSize = leftToRightHints.size(); + var rightToLeftSize = rightToLeftHints.size(); + + //GridModel.log.debug("left to right = " + leftToRightSize); + //GridModel.log.debug("right to left = " + rightToLeftSize); + + if (leftToRightSize > 0 || rightToLeftSize > 0) { + if (leftToRightSize >= rightToLeftSize) { + //for (int hintIndex : rightToLeftHints) { + for (var i = 0; i < rightToLeftHints.length; i++) { + var hintIndex = rightToLeftHints[i]; + this.completedHints.clearRowHint(row, hintIndex); + } + } else { + //for (int hintIndex : leftToRightHints) { + for (var i = 0; i < leftToRightHints.length; i++) { + var hintIndex = leftToRightHints[i]; + this.completedHints.clearRowHint(row, hintIndex); + } + } + } //else { + //throw new RuntimeException("Confusion occured during " + // + "completed hints look-up"); + //} + } + + checkLeftToRight( row) { + var result = []; + + // Current hint we're looking to complete + var currentHintIndex = + GridModel.getFirstHintIndex(this.rowData[row]); + + var currentBox = 0; + + // Current chain of checked boxes + var currentChain = 0; + + while (currentBox < this.boxes.length) { + if (this.boxes[currentBox][row].isChecked()) { + currentChain++; + } else { + // We reach the end of a chain + // And it matches the current hint + if (this.rowData[row][currentHintIndex] == currentChain) { + result.push(currentHintIndex); + currentChain = 0; + + currentHintIndex = + GridModel.getNextHintIndex(currentHintIndex + 1, + this.rowData[row]); + + if (currentHintIndex == GridModel.EMPTY_HINT) { + /* + * If this is the last hint, we verify that + * there aren't any extra checked boxes + */ + for (var i = currentBox; i < this.boxes.length; i++) { + if (this.boxes[i][row].isChecked()) { + // If this is the case, we cancel everything + result.clear(); + break; + } + } + + break; + } + } + + // If there is a blank after a filled hint, we stop there + if (this.boxes[currentBox][row].isEmpty()) { + break; + } + } + + currentBox++; + } + + /* + * If we've reached the end of the row + * and there is a current chain, we mark it as completed + */ + if (currentHintIndex != -1 + && this.rowData[row][currentHintIndex] == currentChain) { + + result.push(currentHintIndex); + } + + return result; + } + + checkRightToLeft( row) { + var result = []; + + var currentHintIndex = GridModel.getLastHintIndex(this.rowData[row]); + var currentBox = this.boxes.length - 1; + var currentChain = 0; + + while (currentBox >= 0) { + if (this.boxes[currentBox][row].isChecked()) { + currentChain++; + } else { + if (this.rowData[row][currentHintIndex] == currentChain) { + result.push(currentHintIndex); + currentChain = 0; + + currentHintIndex = + GridModel.getPreviousHintIndex(currentHintIndex - 1, + this.rowData[row]); + + if (currentHintIndex == -1) { + for (var i = currentBox; i >= 0; i--) { + if (this.boxes[i][row].isChecked()) { + //result.clear(); + result = []; + } + } + + break; + } + } + + if (this.boxes[currentBox][row].isEmpty()) { + break; + } + } + + currentBox--; + } + + return result; + } + + /** + * Checks if a column is empty. + * + * @param col column number + * @return boolean stating wether the column is empty or not + */ + emptyCol( col) { + var index = GridModel.getFirstHintIndex(this.colData[col]); + + return (index == GridModel.getLastHintIndex(this.colData[col])) + && (this.colData[col][index] == 0); + } + + /** + * Checks if a hint has been completed in a column. + * + * @param column column number to check + */ + checkColumn( column) { + //GridModel.log.debug("checkColumn(" + column + ")"); + + if (this.emptyCol(column)) { + this.completedHints.setCompleteCol(column); + return; + } + + var completedColHints = [] + var topToBottomHints = []; + + if (!this.boxes[column][0].isEmpty()) { + topToBottomHints = this.checkTopToBottom(column); + + //completedColHints.addAll(topToBottomHints); + //for (var i = 0; i < topToBottomHints.length; i++) { + completedColHints.push.apply(completedColHints, topToBottomHints); + //} + } + + var bottomToTopHints = []; + + if (!this.boxes[column][this.boxes[0].length - 1].isEmpty()) { + bottomToTopHints = this.checkBottomToTop(column); + + //completedColHints.addAll(bottomToTopHints); + //for (var i = 0; i < bottomToTopHints.length; i++) { + completedColHints.push.apply(completedColHints, bottomToTopHints); + //} + } + + /**/ + var colHints = + this.completedHints.getCompleteColHints(column); + + //for (int hintIndex : colHints) { + for (var i = 0; i < colHints.length; i++) { + var hintIndex = colHints[i]; + + //if (!completedColHints.contains(hintIndex)) { + if (!this.arrayContains(completedColHints, hintIndex)) { + this.completedHints.clearColHint(column, hintIndex); + } + } + + //for (int index : completedColHints) { + for (var i = 0; i < completedColHints.length; i++) { + var index = completedColHints[i]; + this.completedHints.setCompleteColHint(column, index); + } + + /**/ + var complete = true; + + for (var i = 0; i < this.colData[column].length; i++) { + if (this.colData[column][i] != GridModel.EMPTY_HINT && !this.arrayContains(completedColHints, i)) { + complete = false; + } + } + + if (!complete) { + return; + } + /**/ + + var totalChecked = 0; + + for (var i = 0; i < this.boxes[column].length; i++) { + if (this.boxes[column][i].isChecked()) { + totalChecked++; + } + } + + if (totalChecked == this.colHintsTotal[column]) { + return; + } + + var topToBottomSize = topToBottomHints.size(); + var bottomToTopSize = bottomToTopHints.size(); + + if (topToBottomSize > 0 || bottomToTopSize > 0) { + if (topToBottomSize >= bottomToTopSize) { + //for (int hintIndex : bottomToTopHints) { + for (var i = 0; i < bottomToTopHints.length; i++) { + var hintIndex = bottomToTopHints[i]; + this.completedHints.clearColHint(column, hintIndex); + } + } else { + //for (int hintIndex : topToBottomHints) { + for (var i = 0; i < topToBottomHints.length; i++) { + var hintIndex = topToBottomHints[i]; + this.completedHints.clearColHint(column, hintIndex); + } + } + } /*else { + throw new RuntimeException("Confusion occured during " + + "completed hints look-up"); + }*/ + } + + + arrayContains(array, target) { + for (var j = 0; j < array.length; j++) { + if (array[j] == target) { + return true; + } + } + + return false; + } + + + checkTopToBottom( col) { + var result = []; + + var currentHintIndex = + GridModel.getLastHintIndex(this.colData[col]); + var currentBox = 0; + var currentChain = 0; + + while (currentBox < this.boxes[0].length) { + if (this.boxes[col][currentBox].isChecked()) { + currentChain++; + } else { + if (this.colData[col][currentHintIndex] == currentChain) { + result.push(currentHintIndex); + currentChain = 0; + + currentHintIndex = GridModel + .getPreviousHintIndex(currentHintIndex - 1, + this.colData[col]); + + if (currentHintIndex == GridModel.EMPTY_HINT) { + for (var i = currentBox; i < this.boxes[0].length; + i++) { + + if (this.boxes[col][i].isChecked()) { + //result.clear(); + result = []; + break; + } + } + + break; + } + } + + if (this.boxes[col][currentBox].isEmpty()) { + break; + } + } + + currentBox++; + } + + if (currentHintIndex != GridModel.EMPTY_HINT + && this.colData[col][currentHintIndex] == currentChain) { + + result.push(currentHintIndex); + } + + return result; + } + + checkBottomToTop( col) { + var result = []; + + var currentHintIndex = GridModel.getFirstHintIndex(this.colData[col]); + var currentBox = this.boxes[0].length - 1; + var currentChain = 0; + + while (currentBox >= 0) { + if (this.boxes[col][currentBox].isChecked()) { + currentChain++; + } else { + if (this.colData[col][currentHintIndex] == currentChain) { + result.push(currentHintIndex); + currentChain = 0; + + currentHintIndex = + GridModel.getNextHintIndex(currentHintIndex + 1, + this.colData[col]); + + if (currentHintIndex == GridModel.EMPTY_HINT) { + for (var i = currentBox; i >= 0; i--) { + if (this.boxes[col][i].isChecked()) { + //result.clear(); + result = []; + } + } + + break; + } + } + + if (this.boxes[col][currentBox].isEmpty()) { + break; + } + } + + currentBox--; + } + + return result; + } + + /** Checks wether the grid is finished. */ + checkCompleted() { + var completed = true; + + for (var i = 0; i < this.data.length; i++) { + for (var j = 0; j < this.data[i].length; j++) { + var isCurrentBoxChecked = this.boxes[i][j].isChecked(); + + if ((this.data[i][j] && !isCurrentBoxChecked) + || (!this.data[i][j] && isCurrentBoxChecked)) { + + completed = false; + break; + } + } + } + + //if (completed) { + // FIXME + //this.mediator.congratulations(); + //} + return completed; + } + + /** Indicates the current action has come to an end. */ + endAction() { + //this.lastChecked = null; + this.lastModified = null; + } + + /*** Accessors ***/ + + /** + * Returns the boxes. + * + * @return grid boxes + */ + getBoxes() { + return this.boxes; + } + + /** + * Returns the vertical hints. + * + * @return columns hints + */ + getColData() { + return this.colData; + } + + /** + * Returns the horizontal hints. + * + * @return rows hints + */ + getRowData() { + return this.rowData; + } + + /** + * Returns the completed hints. + * + * @return list of completed hints + */ + getCompletedHints() { + return this.completedHints; + } + + clearAll() { + //for (Box[] boxRow : this.boxes) { + // for (Box box : boxRow) { + for (var i = 0; i < this.boxes.length; i++) { + for (var j = 0; j < this.boxes[i].length; j++) { + this.actOnBox(j, i, GridAction.EMPTY); + } + } + } +}; + +GridModel.EMPTY_HINT = -1; Added: trunk/phaser/GridUI.js =================================================================== --- trunk/phaser/GridUI.js (rev 0) +++ trunk/phaser/GridUI.js 2020-03-14 09:39:37 UTC (rev 137) @@ -0,0 +1,708 @@ +/* Copyright Yvan Norsa (2007-2020) + * + * yva...@gm... + * + * This software is a computer program whose purpose is to [describe + * functionalities and technical features of your software]. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redist... [truncated message content] |