基本信息
源码名称:android 九宫格数独游戏
源码大小:0.62M
文件格式:.zip
开发语言:Java
更新时间:2015-09-11
友情提示:(无需注册或充值,赞助后即可获取资源下载链接)
嘿,亲!知识可是无价之宝呢,但咱这精心整理的资料也耗费了不少心血呀。小小地破费一下,绝对物超所值哦!如有下载和支付问题,请联系我们QQ(微信同号):78630559
本次赞助数额为: 2 元×
微信扫码支付:2 元
×
请留下您的邮箱,我们将在2小时内将文件发到您的邮箱
源码介绍
/*
* Copyright (C) 2009 Roman Masek
*
* This file is part of OpenSudoku.
*
* OpenSudoku is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenSudoku is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenSudoku. If not, see <http://www.gnu.org/licenses/>.
*
*/
package cz.romario.opensudoku.game;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Collection of sudoku cells. This class in fact represents one sudoku board (9x9).
*
* @author romario
*
*/
public class CellCollection {
public static final int SUDOKU_SIZE = 9;
/**
* String is expected to be in format "00002343243202...", where each number represents
* cell value, no other information can be set using this method.
*/
public static int DATA_VERSION_PLAIN = 0;
/**
* See {@link #DATA_PATTERN_VERSION_1} and {@link #serialize()}.
*/
public static int DATA_VERSION_1 = 1;
// TODO: An array of ints is a much better than an array of Integers, but this also generalizes to the fact that two parallel arrays of ints are also a lot more efficient than an array of (int,int) objects
// Cell's data.
private Cell[][] mCells;
// Helper arrays, contains references to the groups of cells, which should contain unique
// numbers.
private CellGroup[] mSectors;
private CellGroup[] mRows;
private CellGroup[] mColumns;
private boolean mOnChangeEnabled = true;
private final List<OnChangeListener> mChangeListeners = new ArrayList<OnChangeListener>();
/**
* Creates empty sudoku.
* @return
*/
public static CellCollection createEmpty()
{
Cell[][] cells = new Cell[SUDOKU_SIZE][SUDOKU_SIZE];
for (int r=0; r<SUDOKU_SIZE; r )
{
for (int c=0; c<SUDOKU_SIZE; c )
{
cells[r][c] = new Cell();
}
}
return new CellCollection(cells);
}
/**
* Return true, if no value is entered in any of cells.
*
* @return
*/
public boolean isEmpty() {
for (int r=0; r<SUDOKU_SIZE; r ) {
for (int c=0; c<SUDOKU_SIZE; c ){
Cell cell = mCells[r][c];
if (cell.getValue() != 0)
return false;
}
}
return true;
}
/**
* Generates debug game.
*
* @return
*/
public static CellCollection createDebugGame() {
CellCollection debugGame = new CellCollection(new Cell[][] {
{ new Cell(), new Cell(), new Cell(), new Cell(4), new Cell(5), new Cell(6), new Cell(7), new Cell(8), new Cell(9),},
{ new Cell(), new Cell(), new Cell(), new Cell(7), new Cell(8), new Cell(9), new Cell(1), new Cell(2), new Cell(3),},
{ new Cell(), new Cell(), new Cell(), new Cell(1), new Cell(2), new Cell(3), new Cell(4), new Cell(5), new Cell(6),},
{ new Cell(2), new Cell(3), new Cell(4), new Cell(), new Cell(), new Cell(), new Cell(8), new Cell(9), new Cell(1),},
{ new Cell(5), new Cell(6), new Cell(7), new Cell(), new Cell(), new Cell(), new Cell(2), new Cell(3), new Cell(4),},
{ new Cell(8), new Cell(9), new Cell(1), new Cell(), new Cell(), new Cell(), new Cell(5), new Cell(6), new Cell(7),},
{ new Cell(3), new Cell(4), new Cell(5), new Cell(6), new Cell(7), new Cell(8), new Cell(9), new Cell(1), new Cell(2),},
{ new Cell(6), new Cell(7), new Cell(8), new Cell(9), new Cell(1), new Cell(2), new Cell(3), new Cell(4), new Cell(5),},
{ new Cell(9), new Cell(1), new Cell(2), new Cell(3), new Cell(4), new Cell(5), new Cell(6), new Cell(7), new Cell(8),},
});
debugGame.markFilledCellsAsNotEditable();
return debugGame;
}
public Cell[][] getCells() {
return mCells;
}
/**
* Wraps given array in this object.
* @param cells
*/
private CellCollection(Cell[][] cells)
{
mCells = cells;
initCollection();
}
/**
* Gets cell at given position.
* @param rowIndex
* @param colIndex
* @return
*/
public Cell getCell(int rowIndex, int colIndex) {
return mCells[rowIndex][colIndex];
}
public void markAllCellsAsValid() {
mOnChangeEnabled = false;
for (int r=0; r<SUDOKU_SIZE; r )
{
for (int c=0; c<SUDOKU_SIZE; c )
{
mCells[r][c].setValid(true);
}
}
mOnChangeEnabled = true;
onChange();
}
/**
* Validates numbers in collection according to the sudoku rules. Cells with invalid
* values are marked - you can use getInvalid method of cell to find out whether cell
* contains valid value.
*
* @return True if validation is successful.
*/
public boolean validate() {
boolean valid = true;
// first set all cells as valid
markAllCellsAsValid();
mOnChangeEnabled = false;
// run validation in groups
for (CellGroup row : mRows) {
if (!row.validate()) {
valid = false;
}
}
for (CellGroup column : mColumns) {
if (!column.validate()) {
valid = false;
}
}
for (CellGroup sector : mSectors) {
if (!sector.validate()) {
valid = false;
}
}
mOnChangeEnabled = true;
onChange();
return valid;
}
public boolean isCompleted() {
for (int r=0; r<SUDOKU_SIZE; r )
{
for (int c=0; c<SUDOKU_SIZE; c )
{
Cell cell = mCells[r][c];
if (cell.getValue() == 0 || !cell.isValid()) {
return false;
}
}
}
return true;
}
/**
* Marks all cells as editable.
*/
public void markAllCellsAsEditable() {
for (int r=0; r<SUDOKU_SIZE; r ) {
for (int c=0; c<SUDOKU_SIZE; c ){
Cell cell = mCells[r][c];
cell.setEditable(true);
}
}
}
/**
* Marks all filled cells (cells with value other than 0) as not editable.
*/
public void markFilledCellsAsNotEditable() {
for (int r=0; r<SUDOKU_SIZE; r ) {
for (int c=0; c<SUDOKU_SIZE; c ){
Cell cell = mCells[r][c];
cell.setEditable(cell.getValue() == 0);
}
}
}
/**
* Returns how many times each value is used in <code>CellCollection</code>.
* Returns map with entry for each value.
*
* @return
*/
public Map<Integer, Integer> getValuesUseCount() {
Map<Integer, Integer> valuesUseCount = new HashMap<Integer, Integer>();
for (int value = 1; value <= CellCollection.SUDOKU_SIZE; value ) {
valuesUseCount.put(value, 0);
}
for (int r = 0; r < CellCollection.SUDOKU_SIZE; r ) {
for (int c = 0; c < CellCollection.SUDOKU_SIZE; c ) {
int value = getCell(r, c).getValue();
if (value != 0) {
valuesUseCount.put(value, valuesUseCount.get(value) 1);
}
}
}
return valuesUseCount;
}
/**
* Initializes collection, initialization has two steps:
* 1) Groups of cells which must contain unique numbers are created.
* 2) Row and column index for each cell is set.
*/
private void initCollection() {
mRows = new CellGroup[SUDOKU_SIZE];
mColumns = new CellGroup[SUDOKU_SIZE];
mSectors = new CellGroup[SUDOKU_SIZE];
for (int i=0; i<SUDOKU_SIZE; i ) {
mRows[i] = new CellGroup();
mColumns[i] = new CellGroup();
mSectors[i] = new CellGroup();
}
for (int r=0; r<SUDOKU_SIZE; r )
{
for (int c=0; c<SUDOKU_SIZE; c )
{
Cell cell = mCells[r][c];
cell.initCollection(this, r, c,
mSectors[((c/3) * 3) (r/3)],
mRows[c],
mColumns[r]
);
}
}
}
/**
* Creates instance from given <code>StringTokenizer</code>.
*
* @param data
* @return
*/
public static CellCollection deserialize(StringTokenizer data) {
Cell[][] cells = new Cell[SUDOKU_SIZE][SUDOKU_SIZE];
int r = 0, c = 0;
while (data.hasMoreTokens() && r < 9) {
cells[r][c] = Cell.deserialize(data);
c ;
if (c == 9) {
r ;
c = 0;
}
}
return new CellCollection(cells);
}
/**
* Creates instance from given string (string which has been
* created by {@link #serialize(StringBuilder)} or {@link #serialize()} method).
* earlier.
*
* @param note
*/
public static CellCollection deserialize(String data) {
// TODO: use DATA_PATTERN_VERSION_1 to validate and extract puzzle data
String[] lines = data.split("\n");
if (lines.length == 0) {
throw new IllegalArgumentException("Cannot deserialize Sudoku, data corrupted.");
}
if (lines[0].equals("version: 1")) {
StringTokenizer st = new StringTokenizer(lines[1], "|");
return deserialize(st);
} else {
return fromString(data);
}
}
/**
* Creates collection instance from given string. String is expected
* to be in format "00002343243202...", where each number represents
* cell value, no other information can be set using this method.
*
* @param data
* @return
*/
public static CellCollection fromString(String data) {
// TODO: validate
Cell[][] cells = new Cell[SUDOKU_SIZE][SUDOKU_SIZE];
int pos = 0;
for (int r = 0; r < CellCollection.SUDOKU_SIZE; r ) {
for (int c = 0; c < CellCollection.SUDOKU_SIZE; c ) {
int value = 0;
while (pos < data.length()) {
pos ;
if (data.charAt(pos - 1) >= '0'
&& data.charAt(pos - 1) <= '9') {
// value=Integer.parseInt(data.substring(pos-1, pos));
value = data.charAt(pos - 1) - '0';
break;
}
}
Cell cell = new Cell();
cell.setValue(value);
cell.setEditable(value == 0);
cells[r][c] = cell;
}
}
return new CellCollection(cells);
}
public String serialize() {
StringBuilder sb = new StringBuilder();
serialize(sb);
return sb.toString();
}
/**
* Writes collection to given StringBuilder. You can later recreate the object instance
* by calling {@link #deserialize(String)} method.
* @return
*/
public void serialize(StringBuilder data) {
data.append("version: 1\n");
for (int r=0; r<SUDOKU_SIZE; r )
{
for (int c=0; c<SUDOKU_SIZE; c )
{
Cell cell = mCells[r][c];
cell.serialize(data);
}
}
}
private static Pattern DATA_PATTERN_VERSION_PLAIN = Pattern.compile("^\\d{81}$");
private static Pattern DATA_PATTERN_VERSION_1 = Pattern.compile("^version: 1\\n((?#value)\\d\\|(?#note)((\\d,) |-)\\|(?#editable)[01]\\|){0,81}$");
/**
* Returns true, if given <code>data</code> conform to format of given data version.
*
* @param data
* @param dataVersion
* @return
*/
public static boolean isValid(String data, int dataVersion) {
if (dataVersion == DATA_VERSION_PLAIN) {
return DATA_PATTERN_VERSION_PLAIN.matcher(data).matches();
} else if (dataVersion == DATA_VERSION_1) {
return DATA_PATTERN_VERSION_1.matcher(data).matches();
} else {
throw new IllegalArgumentException("Unknown version: " dataVersion);
}
}
public void addOnChangeListener(OnChangeListener listener) {
if (listener == null) {
throw new IllegalArgumentException("The listener is null.");
}
synchronized (mChangeListeners) {
if (mChangeListeners.contains(listener)) {
throw new IllegalStateException("Listener " listener "is already registered.");
}
mChangeListeners.add(listener);
}
}
public void removeOnChangeListener(OnChangeListener listener) {
if (listener == null) {
throw new IllegalArgumentException("The listener is null.");
}
synchronized (mChangeListeners) {
if (!mChangeListeners.contains(listener)) {
throw new IllegalStateException("Listener " listener " was not registered.");
}
mChangeListeners.remove(listener);
}
}
/**
* Returns whether change notification is enabled.
*
* If true, change notifications are distributed to the listeners
* registered by {@link #addOnChangeListener(OnChangeListener)}.
*
* @return
*/
// public boolean isOnChangeEnabled() {
// return mOnChangeEnabled;
// }
//
// /**
// * Enables or disables change notifications, that are distributed to the listeners
// * registered by {@link #addOnChangeListener(OnChangeListener)}.
// *
// * @param onChangeEnabled
// */
// public void setOnChangeEnabled(boolean onChangeEnabled) {
// mOnChangeEnabled = onChangeEnabled;
// }
/**
* Notify all registered listeners that something has changed.
*/
protected void onChange() {
if (mOnChangeEnabled) {
synchronized (mChangeListeners) {
for (OnChangeListener l : mChangeListeners) {
l.onChange();
}
}
}
}
public interface OnChangeListener {
/**
* Called when anything in the collection changes (cell's value, note, etc.)
*/
void onChange();
}
}