/* * Copyright © 2012 Nokia Corporation. All rights reserved. * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. * Oracle and Java are trademarks or registered trademarks of Oracle and/or its * affiliates. Other product and company names mentioned herein may be trademarks * or trade names of their respective owners. * See LICENSE.TXT for license information. */ package com.nokia.example.sudokumaster; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image; import javax.microedition.lcdui.game.TiledLayer; public class SudokuView extends View { private static final int BG0 = 1; private static final int BG1 = 2; private static final int BG0_HIGHLIGHTED = 3; private static final int BG1_HIGHLIGHTED = 4; private static final int BG_ERROR = 5; private static final int ANIMATION_DURATION = 20; private static final int NUMBER_COLOR = 0x000000ff; private static final int PUZZLE_NUMBER_COLOR = 0x00000000; private static final int BACKGROUND_COLOR = 0x004e2316; private int[][] numbers = new int[9][9]; private int[][] puzzle = new int[9][9]; private int[][] animate = new int[9][9]; private TiledLayer board; private Image boardImage; private volatile boolean refreshBoard = true; private Listener listener; private int tileSize; private int selectedCol = 4, selectedRow = 4; private int moves = 0; private long startTime; private long victoryTimeSeconds = -1; public SudokuView(Listener listener) { super(); this.listener = listener; } public void setBoardImage(Image image) { board = null; boardImage = image; refreshBoard = true; } public void setBoardSize(int boardSize) { setSize(boardSize, boardSize); tileSize = boardSize / 9; } public void update() { for (int col = 0; col < 9; col++) { for (int row = 0; row < 9; row++) { if (animate[col][row] > 0) { boolean old = drawError(col, row); animate[col][row]--; if (old != drawError(col, row)) { refreshBoard = true; invalidate(); } } } } } private boolean drawError(int col, int row) { return 4 * animate[col][row] / ANIMATION_DURATION % 2 == 1; } protected void paint(Graphics g) { drawBoard(g); drawNumbers(g); } private void drawBoard(Graphics g) { g.setColor(BACKGROUND_COLOR); g.fillRect(left, top, width, height); if (boardImage == null) { return; } if (board == null) { board = new TiledLayer(9, 9, boardImage, boardImage.getWidth() / 5, boardImage.getHeight()); board.setPosition(left, top); } if (refreshBoard) { refreshBoard(); } board.paint(g); } private void refreshBoard() { refreshBoard = false; board.fillCells(0, 0, 9, 9, BG0); board.fillCells(0, 3, 3, 3, BG1); board.fillCells(3, 0, 3, 3, BG1); board.fillCells(3, 6, 3, 3, BG1); board.fillCells(6, 3, 3, 3, BG1); if (selectedCol >= 0 && selectedRow >= 0) { for (int col = 0; col < 9; col++) { if (col != selectedCol) { final int bg = board.getCell(col, selectedRow) == BG0 ? BG0_HIGHLIGHTED : BG1_HIGHLIGHTED; board.setCell(col, selectedRow, bg); } } for (int row = 0; row < 9; row++) { if (row != selectedRow) { final int bg = board.getCell(selectedCol, row) == BG0 ? BG0_HIGHLIGHTED : BG1_HIGHLIGHTED; board.setCell(selectedCol, row, bg); } } } for (int col = 0; col < 9; col++) { for (int row = 0; row < 9; row++) { if (animate[col][row] > 0 && drawError(col, row)) { board.setCell(col, row, BG_ERROR); } } } } private void drawNumbers(Graphics g) { g.setColor(PUZZLE_NUMBER_COLOR); final int xOffset = left + (tileSize + 1) / 2; final int yOffset = top + (tileSize + 1) / 2 - g.getFont().getHeight() / 2; int x, y, n; for (int col = 0; col < 9; col++) { x = xOffset + col * tileSize; for (int row = 0; row < 9; row++) { y = yOffset + row * tileSize; n = puzzle[col][row]; if (n > 0) { g.drawString(String.valueOf(n), x, y, Graphics.HCENTER | Graphics.TOP); } } } g.setColor(NUMBER_COLOR); for (int col = 0; col < 9; col++) { x = xOffset + col * tileSize; for (int row = 0; row < 9; row++) { y = yOffset + row * tileSize; n = numbers[col][row]; if (n > 0) { g.drawString(String.valueOf(n), x, y, Graphics.HCENTER | Graphics.TOP); } } } } public void handlePointerEvent(int type, int x, int y) { if (!isVisible() || !hits(x, y)) { return; } if (tileSize > 0) { selectCell(Math.min((x - left) / tileSize, 8), Math.min( (y - top) / tileSize, 8)); } if (type == View.POINTER_RELEASED && puzzle[selectedCol][selectedRow] <= 0) { listener.onCellSelected(); } } private void selectCell(int col, int row) { selectedCol = col; selectedRow = row; refreshBoard = true; invalidate(); } public void keyEvent(final int type, final int key) { if (type == KEY_RELEASED) { return; } switch (key) { case KEY_UP: selectCell(selectedCol, (selectedRow - 1 + 9) % 9); break; case KEY_DOWN: selectCell(selectedCol, (selectedRow + 1 + 9) % 9); break; case KEY_LEFT: selectCell((selectedCol - 1 + 9) % 9, selectedRow); break; case KEY_RIGHT: selectCell((selectedCol + 1 + 9) % 9, selectedRow); break; case KEY_SELECT: if (type == KEY_PRESSED && puzzle[selectedCol][selectedRow] <= 0) { listener.onCellSelected(); } break; default: break; } } public void setNumber(final int n) { if (cellSelected() && puzzle[selectedCol][selectedRow] < 1) { final int newNumber = n > 9 || n < 1 ? 0 : n; if (validateNumber(n) && numbers[selectedCol][selectedRow] != newNumber) { numbers[selectedCol][selectedRow] = newNumber; moves++; if (isComplete()) { victoryTimeSeconds = getElapsedSeconds(); } invalidate(); } } listener.onSetNumber(); } private boolean cellSelected() { return selectedCol >= 0 && selectedCol < 9 && selectedRow >= 0 && selectedCol < 9; } private int getNumber(final int col, final int row) { if (puzzle[col][row] > 0) { return puzzle[col][row]; } return numbers[col][row]; } private boolean validateNumber(final int n) { return n == 0 || (validateRow(n) & validateCol(n) & validateBlock(n)); } private boolean validateRow(final int n) { for (int col = 0; col < 9; col++) { if (getNumber(col, selectedRow) == n && col != selectedCol) { animateCell(col, selectedRow); return false; } } return true; } private boolean validateCol(final int n) { for (int row = 0; row < 9; row++) { if (getNumber(selectedCol, row) == n && row != selectedRow) { animateCell(selectedCol, row); return false; } } return true; } private boolean validateBlock(final int n) { final int colOffset = selectedCol / 3 * 3; final int rowOffset = selectedRow / 3 * 3; int col, row; for (int c = 0; c < 3; c++) { col = colOffset + c; for (int r = 0; r < 3; r++) { row = rowOffset + r; if (getNumber(col, row) == n && !(col == selectedCol && row == selectedRow)) { animateCell(col, row); return false; } } } return true; } private void animateCell(int col, int row) { animate[col][row] = ANIMATION_DURATION; refreshBoard = true; invalidate(); } public boolean isComplete() { int n; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { n = getNumber(i, j); if (n <= 0) { return false; } } } return true; } public static interface Listener { void onCellSelected(); void onSetNumber(); } public void restart() { numbers = new int[9][9]; moves = 0; startTime = System.currentTimeMillis(); victoryTimeSeconds = -1; selectCell(4, 4); } public void newGame(int[][] puzzle) { this.puzzle = puzzle; restart(); } public int getMoves() { return moves; } public int getEmpty() { int empty = 0; int n; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { n = getNumber(i, j); if (n <= 0) { empty++; } } } return empty; } public long getElapsedSeconds() { if (victoryTimeSeconds > -1) { return victoryTimeSeconds; } return (System.currentTimeMillis() - startTime) / 1000; } private void setElapsedSeconds(long elapsedSeconds) { if (isComplete()) { victoryTimeSeconds = elapsedSeconds; } startTime = System.currentTimeMillis() - elapsedSeconds * 1000; } public byte[] getState() { ByteArrayOutputStream bout = null; try { bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { dout.writeByte(puzzle[i][j]); dout.writeByte(numbers[i][j]); } } dout.writeByte(selectedCol); dout.writeByte(selectedRow); dout.writeInt(moves); dout.writeLong(getElapsedSeconds()); return bout.toByteArray(); } catch (IOException e) { return new byte[0]; } finally { try { if (bout != null) { bout.close(); } } catch (IOException e) { } } } public void setState(byte[] state) { try { DataInputStream din = new DataInputStream(new ByteArrayInputStream(state)); for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { puzzle[i][j] = din.readByte(); numbers[i][j] = din.readByte(); } } selectedCol = din.readByte(); selectedRow = din.readByte(); moves = din.readInt(); setElapsedSeconds(din.readLong()); } catch (IOException e) { } } }