📄 sudokume.java
字号:
package sudoku;
import java.util.*;
import java.io.IOException;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
/*
* Copyright, 2005, S.E.Morris, C.Speirs
*
* This file is part of SudokuME.
*
* SudokuME 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 2 of the License, or
* (at your option) any later version.
*
* SudokuME 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 SudokuME; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// Stuff to do:
// X) Check if records can be saved to store (enough space).
// X) Scrolling on record selection page
// X) Downloadable puzzles
// X) CatEntry should only load when tab selected
// X) Puzzle solver
// -) Puzzle generator
// -) make solver select a random solution if multiple are possible
// -) to generate, add locked cells until there is only a single solution.
// -) Improved puzzle generator
// -) make solver able to rate a game
// -) use the rating to filter the found games.
// X) Optmised load/save
// X) Replace startGrid with lock booleans
// X) Better list support - with download waiting
// X) Credits/copyright
// X) Timer
// X) Titles in save game
// X) Compression on online content
// -) Better error reporting
// X) Completion check
// X) Sub menus in cat.txt
// X) Undo feature
// -) Improve undo - undo working and save undo.
// X) Rename save title
// X) Improve UI - Allow user to identify if working list is complete or not - press * when editing working - changes ? to .
// X) Improve UI - If user has separate left/right/up/down/working sel then allow movement in grid when working is selected
// *) Allow saved games to be loaded from elsewhere (file browser) - useful if peoples telco doesnt
// allow external access
// X) make keys user programmable (and savable) - allows for user friendly keys on telephones that support it
// Keys needed: menu, Shift, left, right, up, down, have<1-9>, undo, working, timer, lock_working, lock_cell, increment
// UI as follows: list of keys. soft keys to clear or program. last option: done. When starting
// a game, check that enough keys are available - menu accessible, increment or 1-9 (resp 0-9,*,#) available.
// -) allow different keys for a 12x12 game and a 9x9 game
// X) Work out UI for 12x12 grid (using *,0,#)
// X) on forced exit, save the game.
// X) can we make it full screen?
// X) If screen is wider than it is tall, then put working on the side
// X) Allow user to select full screen mode, if MIDP2 is supported
// -) Zoomed working mode? - View working for all cells in a row/column/group?
// -) Improve performance by using GameUI?
// X) To identify the phone, use System.getProperty("microedition.platform")
// -) Improve start up menus - use normal hierarchical system. main menu - (Continue) / New game(9x9,12x12,test,load from file, load from online) / load saved game / Options (set keys, set fullscreen, set online catalog) / exit
// Main Menu: List: [Continue],[End],New Game,Instructions,Options
// New Game: List: 9x9, 12x12,Test Grid, Load from File, Load from online
// Load from file: To be implemented
// Load from online: Investigate how to link to current code
// Instructions: List: Rules, Key Definitions, Programming Keys, About
// Options: Load ProgKeys list/display.
//
//
// *********************************************************************
// Main class.
// *********************************************************************
public class SudokuME extends MIDlet implements MenuListener,GameSelectionListener
{ // -----State data
private GameData game; // Game data object
public static ProgKeys programmedKeyCodes;
private long timeStarted; // Timer working values
private long timeAway,timeAwayStart; // Ditto
private int mode=MENU; // Current display mode
private final static int MENU=0; // Mode 0 : load/new menu
private final static int GRID=1; // Mode 1 : game
private final static int TOPLEFT = Graphics.TOP|Graphics.LEFT;
private final static int CENTERED = Graphics.TOP|Graphics.HCENTER;
final static byte FORMAT_VERSION = 4;
final static byte SAVE_GAME_RECORD = 1;
final static byte KEYCODE_RECORD = 2;
final static String REC_STORE_NAME = "SudokuME";
// -----Transient data. Can be deleted when paused.
public static Display display; // Our display (handy!)
static boolean debug=false; // Switch on dumping of exceptions
static String catalogueAddr=null; // Location of on-line catalogue (from JAD)
static OnlineDirectory onlineCatalogue; // Online oatalogue root
static OnlineEntry selectedBundle; // Download bundle selection
static OnlineDirectory selectedOnlineCatalogueDir; // Current catalogue directory
static boolean hasMIDP2 = false;
// -----------------------------------------------------------------
// MIDlet lifecycle.
// -----------------------------------------------------------------
public void startApp() throws MIDletStateChangeException
{ // -----Create display
display = Display.getDisplay(this);
programmedKeyCodes = new ProgKeys();
if (System.getProperty("microedition.profiles").indexOf("MIDP-2") != -1) {
hasMIDP2 = true;
} else {
hasMIDP2 = false;
}
// -----Location of Catalogue file
catalogueAddr = getAppProperty("SudokuME-Catalogue-Addr");
debug = getAppProperty("SudokuME-Debug").equals("true");
// -----Set display based upon current mode
Displayable displayable=null;
switch(mode)
{ case MENU :
displayable = new GameSelection(display,this);
break;
case GRID :
displayable = new GridDisplay();
break;
}
display.setCurrent(displayable);
}
public void pauseApp()
{ // -----Release all non-essensial resources here (anything we can
// -----recreate later on).
if(mode==GRID) pauseTimer();
// -----See footnote 1 at the bottom of this file.
if(mode==MENU) ((GameSelection)display.getCurrent()).destroy();
display=null; catalogueAddr=null;
onlineCatalogue=null; selectedBundle=null;
selectedOnlineCatalogueDir=null;
OnlineEntry.reset();
}
public void destroyApp(boolean unconditional)
{ pauseApp();
if(!unconditional) { // -----Ask if we want to save first
final String[][] optStr = { { "Discard game","Save before exiting" } };
final int[][] optInt = { { 100,0 } };
new Menu(display,this,optStr,optInt,-1,-1);
// 'game' gets set to null by menu handler
} else { // -----Just get the hell out of here, pronto
game.save();
game=null;
}
}
public void menuSelection(int code)
{ if(code==100) game.save();
game=null;
}
// -----------------------------------------------------------------
// These three methods allow us to time how long a player takes to
// play a grid. This is much more problematic than it would seem, as
// we have to take account of pauses in the action - for example when
// menus are accessed, the "About screen" is shown, or the game is
// interrupted (startApp/pauseApp).
//
// We do this my maintaining two variables: one which is the time play
// started (timeStarted) and the other the total amount of time away
// from the action (timeAway), excluding the current period if currently
// paused. A third (timeAwayStarted) is used to time each period of
// inaction, which is added to the total once the period ends.
//
// resetTimer() should be used when entering a game.
// pauseTimer() should be used when entering an untimed period.
// startTimer() should be used to start/restart the timer.
// -----------------------------------------------------------------
private synchronized void resetTimer()
{ //System.out.println("r");
// -----Set start time *BUT* make allowances for time already
// -----played and saved in save game record (stored as seconds).
timeStarted=System.currentTimeMillis()-game.timer*1000;
timeAwayStart=0; timeAway=0;
}
private synchronized void startTimer()
{ //System.out.println("s");
// -----Are we paused? Unpause and add on current inaction period to
// -----total time away, then reset current period.
if(timeAwayStart>0)
{ timeAway += System.currentTimeMillis()-timeAwayStart;
}
timeAwayStart=0;
}
private synchronized void pauseTimer()
{ //System.out.println("p");
// -----Are we unpaused? (Don't pause if already paused!)
// -----Create new period of inaction.
if(timeAwayStart==0)
timeAwayStart=System.currentTimeMillis();
}
private synchronized long getTimer()
{ // -----Current time
long curr=System.currentTimeMillis();
// -----Current period of inaction (if paused)
long away = (timeAwayStart>0) ? curr-timeAwayStart : 0;
// -----Current time minus time started minus inaction total
// -----minus current period of inaction.
return curr-timeStarted-timeAway-away;
}
private String getTimerString()
{ int sec = (int)(getTimer()/1000);
StringBuffer sb = new StringBuffer("Timer: ");
sb.append(sec/60).append(":").append((sec%60<10)?"0":"").append(sec%60);
return sb.toString();
}
// -----------------------------------------------------------------
// GameSelection listener: called once a game has been selected
// -----------------------------------------------------------------
public void gameSelected(GameData gd)
{ game=gd; mode=GRID; resetTimer();
display.setCurrent(new GridDisplay());
}
// *****************************************************************
// The game grid. This component uses optimised drawing to attempt
// to speed up the display of the grid, which can have 81 or 144
// cells depending upon size, and therefore will draw slowly on low
// spec phones. To do this an array is used which holds a int
// representation of what is currently drawn to a cell, in the format...
// 0001 0000 0000 : (1 bit) 1=cursor at cell, 0=no cursor at cell
// 0000 1111 1100 : (6 bits) tile x in 'digits' image
// 0000 0000 0011 : (2 bits) tile y in 'digits' image
// For each cell the cursor flag, x and y digits' offset are calculated
// and compared to the current contents. Only if different will the
// cell be actually plotted. Note: a master switch, optPaintAll, is
// used to draw the entire grid initially, eg: when first displayed or
// when removing an on-screen menu.
// *****************************************************************
private class GridDisplay extends Canvas implements MenuListener, CommandListener
{ private int xOff,yOff; // Position of grid, centred
private int workingH,workingY; // Position of working out workingH/W is the height/width added to the grid
private int workingW,workingX; // Position of working out workingX/Y is the position of the first cell
private boolean horizWorking=true; // Is the working grid plotted horizontally or vertically (to side of grid if display is wider than it is tall)
private int cellW,cellH; // Height/width of a single cell
private int cursorX,cursorY; // Current x/y of cursor on grid
private int workCursorX; // Working out cursor
private Image digits; // Digits image
private boolean editGrid=true; // True=grid, false=working
private boolean shiftFlag=false; // Holding down Game B key?
private boolean showTimer=false; // Show timer on main display
private int[][] optPaintPrevious; // Optimised painting of cells
private int[] optPaintPrevious2; // Optimised painting of working
private boolean optPaintAll=true; // Master switch - paint all, inc. background
private int optPaintDebug=0; // Count cells painted
private byte undoCurrent; // Value of current cell
private short[] undoBuffer; // Undo buffer value:8,x:4,y:4
private int undoPosition; // Undo buffer position
private KeyHelper keyHelper;
private byte workingDotX[],workingDotY[],workingDotW[],workingDotH[];
private final String[][] menuStr =
{ { "Save","About","Options","Game","Exit","Cancel" },
{ "Yes","No" },
{ "Yes","No" },
{ "","Save","","Don't save","","Cancel" },
{ "Lock all","Unlock all","Cancel" },
{ "Recreate","Tidy locked","Tidy numbers","Tidy all","Cancel" },
{ "Undo","Locks","Working","Solver","Cancel" },
{ "Solved?","Solvable?","Solve","Hint","Fix Errors", "Cancel"},
{ "AutoWork","InCellWork","BlankWork","Cancel" }
};
private final int[][] menuInt =
{ { 100,500,-8,-6,-3,0 },
{ 200,0 },
{ 300,0 },
{ 0 , 401 , 0 , 402 , 0,0 },
{ -1,-2,0 },
{ 600,601,602,603,0 },
{ 800,-4,-5,-7,0 },
{ 700, 701, 702, 703, 704, 0},
{ 900, 901, 902,0}
};
// -------------------------------------------------------------
// CONSTRUCTOR
// -------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -