游戲策劃咨訊
          做一個游戲并不難,難的是做一個好游戲;完美在于積累!

          本文通過一個簡單的MIDlet游戲程序示例,簡要介紹了MIDlet圖形編程.....

          介紹

            本文通過一個簡單的MIDlet游戲程序示例,簡要介紹了MIDlet圖形編程,以期能對開發者深入理解MIDP圖形編程和開發復雜的移動游戲有所幫助。

            一. MIDLET圖形

            1、MIDlet 圖形簡述

            移動信息設備描述(Mobile Information Device Profile ,MIDP)定義了一套應用編程接口(API),用于運行在MIDP容器中的MIDlet應用程序。這套API本身是建立在有限連接設備配置(Connected Limited Device Configuration ,CLDC)應用編程接口的基礎上的。MIDP用戶界面應用編程接口類并不是基于Java抽象窗口工具包(Abstract Window Toolkit ,AWT)設計。它們是專為手機和呼機這樣的小型移動信息設備而設計的,這類設備的特點是只有很小的屏幕和鍵盤。當一個程序員在編寫MIDP圖形應用程序的時候,他可能只能使用MIDP或CLDC應用編程接口。

            MIDP的中心抽象是屏幕,這句話的含義是MIDP的用戶界面設計是基于屏幕的(screen-based)。也就是說,Screen類封裝了設備特定的圖形和用戶交互,所有的用戶界面組件都位于屏幕上,并且一次只顯示一個屏幕,并且只能瀏覽或使用這個屏幕上的條目。由屏幕來處理所有的用戶界面事件。并只把高級事件傳送給應用。之所以采取這種面向屏幕(screen-oriented) 的方式,主要是因為移動設備的顯示屏幕和鍵盤實是種類太多了,幾乎每個廠家都多多少少有所不同。圖1是基于屏幕的MIDP圖形用戶界面的一些例子。


          圖1:基于屏幕的MIDP 圖形用戶界面

            MIDP 應用編程接口具有高級用戶界面類和低級用戶界面類。高級用戶界面類(例如Form、List、TextBox、TextField、Alert,及Ticker)可被適配到設備上:支持圖像、文本、文本輸入域、單選按鈕等。低級用戶界面類(Canvas類)允許開發者根據需要繪制任意圖形。MIDlet可以運行在各種不同尺寸的彩色、不同灰度等級或黑白屏幕的手機上。高級用戶界面類是通用用戶界面元素的抽象,它的用途在于提高MIDlet跨不同設備的移植性,并且可以使用本地設備的外觀表現。低級應用編程接口則能夠更直接地控制顯示內容,但是MIDlet設計者應該確保其在不同設備(顯示尺寸、鍵盤、色彩等)上的可移植性。上面的例子既用到了高級應用編程接口又用到了低級應用編程接口。

            所有的MIDP圖形用戶界面類都是javax.microedition.lcdui程序包的一部分。

            2、MIDlet屏幕

            MIDP有兩種主要的屏幕類型:

            A 高級屏幕

             它包括簡單的高級屏幕類,例如List和TextBox。用戶不能添加額外的圖形用戶界面組件到這種類型的屏幕中。九宮格MIDlet示例程序使用的屏幕是繼承于名為ChoosePieceScreen的List類,用于游戲者在游戲開始時選擇棋子。

             一般的Form屏幕類和List類很相像,但是它允許使用額外的圖形元素,例如:圖像、只讀文本域、可編輯文本域、可編輯數據域、標尺和選項組。Form條目可以任意地被添加或刪除。九宮格例程中沒有使用Form類。

            B 低級屏幕

             Canvas(畫布)屏幕(和Graphics、Image類) 可以用來編寫基于低級應用編程接口的用戶界面。這些類給予MIDlet程序員很大程度的繪畫靈活性。程序員可以繪制各種類型的圖形元素,例如:線、弧、矩形、圓角矩形、圓、文字(不同顏色、字體、大小)、位圖剪輯等等。大部分的游戲MIDlet是使用基于畫布屏幕類的主圖形用戶界面元素編寫的。

            一個MIDlet用戶界面通常包含一個或多個屏幕。因為每次只能顯示一個屏幕,因此MIDlet具有良好設計的結構是非常重要的進行,這樣就能更加容易地處理屏幕之間內容的切換。

            下面的代碼段說明了在一個MIDlet中切換屏幕的方法,基于屏幕類和對應的MIDlet回調。

            代碼段1:

          Class MyMIDlet extends MIDlet
          {
          private FirstScreen firstScreen;
          private SecondScreen secondScreen;
          public MyMIDlet()
          {

          }
          public void startApp()
          {
          Displayable current = Display.getDisplay(this).getCurrent();
          if (current == null)
          {
          firstScreen = new FirstScreen(this, …);
          Display.getDisplay(this).setCurrent(firstScreen);
          //顯示應用程序的第一個用戶界面屏幕
          }
          else
          {
          Display.getDisplay(this).setCurrent(current);
          }
          }
          // FirstScreen 回調切換到下一個屏幕
          public void firstScreenDone()
          {

          secondScreen = new SecondScreen(this, …);
          display.getDisplay(this).setCurrent(secondScreen);
          }
          // SecondScreen回調終止應用程序
          public void secondScreenQuit()
          {

          destroyApp(false);
          notifyDestroyed();
          }

          }


            這個MIDlet使用了兩個屏幕類(FirstScreen和SecondScreen)作為用戶界面。當開始執行MIDlet的時候,它設置當前顯示屏幕為FirstScreen。當需要從FirstScreen切換到SecondScreen的時候,FirstScreen 調用父MIDlet方法firstScreenDone(參見下面的代碼)。firstScreenDone方法創建并設置SecondScreen為當前顯示的屏幕。

            代碼段2: 包含MIDlet回調的FirstScreen示例

          Class FirstScreen extends Form implements CommandListener {
          private MyMIDlet midlet;
          public FirstScreen(MyMIDlet midlet)
          {
          this.midlet = midlet;

          }
          public void commandAction(Command c)
          {
          if (c == cmdQuit)
          {
          parent.firstScreenDone();
          }

          }

          }


            3、MIDP用戶界面應用編程接口

            保證基于高級應用編程接口類的用戶界面對象的可移植性和適用性是MIDP設備的職責。

            另一方面,像Canvas和Graphics這樣的低級類為程序員提供了更大的自由空間讓其控制其用戶界面的視覺表現,并且監聽低級鍵盤事件。程序員還要負責確保應用程序在不同特性(例如顯示尺寸、彩色或黑白,以及不同鍵盤類型)的移動設備上的可移植性。比如說,有可能需要使用getWidth()和getHeight()方法調節用戶界面外觀使其適應一個或更多設備的可用Canvas尺寸。

            下面的九宮格MIDlet例程將介紹:

             簡單應用高級應用編程接口;

             使用低級應用編程接口來繪制線、弧、字符串和圖像等圖形;

             不同顯示尺寸的移動設備之間的MIDlet移植問題

             鍵盤代碼與游戲動作之間的映射

            本章概述了MIDP圖形用戶界面的設計,如果想得到更進一步的信息,請參閱
          http://java.sun.com/products/midp/ 。

            二. 示例:九宮格(TICTACTOEMIDLET)

            1、設計

            概述

            這個示例應用程序是一個簡單的MIDlet,允許游戲者與MIDlet程序之間玩一種稱為九宮格的人機游戲。這個例程說明:

             使用高級和低級用戶界面組件

             在多顯示屏幕之間進行切換

             處理簡單的命令

             動態適配顯示尺寸
           
             處理鍵盤事件

            游戲者首先選擇使用哪種棋子(用圓和叉表示),然后開始游戲。游戲者和MIDlet誰是先手是隨機決定的。每走一步棋之后,程序都要檢查游戲狀態,判斷游戲是否已經結束。游戲的幾種可能結果是:游戲者贏,MIDlet程序贏,或者平局。在應用程序運行期間,雙方的得分都能顯示出來。游戲者可以隨時開始新游戲或者退出游戲。 

            圖2:所示的屏幕快照是游戲中的MIDlet用戶界面。


          圖2:游戲屏幕的先后順序

            2、九宮格MIDlet

            下面是九宮格MIDlet的類模式圖:


          圖3:九宮格MIDlet類圖

            當MIDlet啟動方法startApp()時,將創建閃爍屏幕和第一個游戲屏幕(ChoosePieceScreen)。閃爍屏幕顯示4秒之后,第一個游戲屏幕開始顯示。ChoosePieceScreen讓游戲者選擇使用哪種棋子(圓還是叉)。當游戲者做出選擇之后,他可以使用OK鍵確認。這會使ChoosePieceScreen回調主MIDlet的choicePieceScreenDone()方法。

            ChoosePieceScreen是使用高級應用編程接口List類實現的。 

           
          圖4:ChoosePieceScreen是一個高級用戶界面List子類

            choosePieceScreenDone()回調創建并顯示下一個屏幕,這個屏幕在此應用程序中作為游戲的主屏(GameScreen)。

            每當輪到游戲者下棋的時候,游戲者使用GameScreen的箭頭鍵和Select按鈕來選擇想要走的空格。每一回合之后,應用程序都會檢查游戲的狀態,檢查其是否符合游戲結束條件并顯示游戲結果。游戲者通過點擊GameScreen的Quit命令結束游戲,或使用New命令開始新一輪游戲。Quit(結束)命令調用TicTacToeMIDlet的quit()方法,然后MIDlet就會調用destroyApp()方法來終止整個MIDlet程序。

            游戲程序邏輯被封裝在一個單獨的Game類中。本文只關注MIDlet的圖形設計,而對游戲程序邏輯不作深入探討。如果要與現有的applet Java程序作比較,請參閱http://java.sun.com/applets/jdk/1.0/demo/TicTacToe/TicTacToe.java 和http://java.sun.com/products/jfc/tsc/articles/tictactoe/index.html 中的游戲程序邏輯。
          GameScreen通過使用低級Canvas和Graphics類來實現。它使用Canvas、Image和Graphics對象來繪制圖形。
          GameScreen首先初始化基于畫布尺寸的顯示面板。這可讓MIDlet能夠運行在不同顯示屏幕的移動設備上。在本例中還使用了一個Image對象用來表示游戲面板。然后GameScreen根據游戲者在ChoosePieceScreen中所做的選擇為游戲者和MIDlet分配棋子。游戲然后進行初始化(包括隨機決定誰是先手),然后游戲就開始了。
          為了使GameScreen能夠被移植,MIDlet的鍵盤代碼必須被映射到游戲動作上,如:Up、Down、Left、Right和Fire,用于具有不同鍵盤的移動設備。每當一個鍵被按下的時候,keyPressed()方法就會判斷這是一個方向鍵還是一個Fire/Select鍵。如果按下的鍵是方向鍵,光標就會相應地移動,幫助游戲者可視化地選擇一個空格放入棋子。Select鍵用來選擇一個空格放入棋子。如果探測到滿足游戲結束的條件,就會顯示一條信息宣布游戲的獲勝者和本輪游戲的得分。(見下圖)

           
          圖5: GameScreen 是一個低級 Canvas(畫布)子類

            3、TicTacToeMIDlet.java

            TicTacToeMIDlet非常簡單:它處理MIDlet的生命周期事件。它根據需要創建屏幕對象并且處理來自屏幕的回調。ChoosePieceScreenDone回調被用來創建GameScreen。quit方法則被GameScreen用來結束游戲。

          package example.tictactoe;
          import java.io.IOException;
          import javax.microedition.midlet.*;
          import javax.microedition.lcdui.*;
          import javax.microedition.io.*;
          public class TicTacToeMIDlet extends MIDlet {
          private ChoosePieceScreen choosePieceScreen;
          private GameScreen gameScreen;
          public TicTacToeMIDlet()
          {
          }
          public void startApp() {
          Displayable current = Display.getDisplay(this).getCurrent();
          if (current == null) {
          // first time we've been called
          // Get the logo image
          Image logo = null;
          try
          {
          logo = Image.createImage("/tictactoe.png");
          }
          catch (IOException e) {
          // just use null image
          }
          Alert splashScreen = new Alert(null, "Tic-Tac-Toe\nForum Nokia", logo, AlertType.INFO);


          splashScreen.setTimeout(4000);
          // 4 seconds
          choosePieceScreen = new ChoosePieceScreen(this);
          Display.getDisplay(this).setCurrent(splashScreen, choosePieceScreen);
          }
          else
          {
          Display.getDisplay(this).setCurrent(current);
          }
          }
          public void pauseApp() {
          }
          public void destroyApp(boolean unconditional) {
          }
          public void quit()
          {
          destroyApp(false);
          notifyDestroyed();
          }
          public void choosePieceScreenDone(boolean isPlayerCircle)
          {
          gameScreen = new GameScreen(this, isPlayerCircle);
          Display.getDisplay(this).setCurrent(gameScreen);
          }
          }

            4、ChoosePieceScreen.java

            ChoosePieceScreen是一個基于高級應用編程接口窗體的屏幕,允許游戲者選擇圓或叉作為棋子。當游戲者按下OK鍵時,它使用MIDlet的回調方法choosePieceScreenDone來處理游戲者的選擇。

          package example.tictactoe;
          import javax.microedition.midlet.*;
          import javax.microedition.lcdui.*;
          import javax.microedition.io.*;
          public class ChoosePieceScreen extends List implements CommandListener
          {
          private static final String CIRCLE_TEXT = "Circle";
          private static final String CROSS_TEXT = "Cross";
          private final TicTacToeMIDlet midlet;
          private final Command quitCommand;
          public ChoosePieceScreen(TicTacToeMIDlet midlet) {
          super("Choose your piece", List.IMPLICIT);
          this.midlet = midlet;
          append(CIRCLE_TEXT, loadImage("/circle.png"));
          append(CROSS_TEXT, loadImage("/cross.png"));
          quitCommand = new Command("Quit", Command.EXIT, 2);
          addCommand(quitCommand);
          setCommandListener(this);
          }
          public void commandAction(Command c, Displayable d) {
          boolean isPlayerCircle = getString(getSelectedIndex()).equals(CIRCLE_TEXT);
          if (c == List.SELECT_COMMAND) {
          midlet.choosePieceScreenDone(isPlayerCircle);
          }
          else
          // quit Command
          {
          midlet.quit();
          }
          }
          private Image loadImage(String imageFile)
          {
          Image image = null;
          try

          {
          image = Image.createImage(imageFile);
          }
          catch (Exception e)
          {
          // Use a 'null' image in the choice list (i.e. text only choices).
          }
          return image;
          }
          }

            5、GameScreen.java

            GameScreen使用了一個低級應用編程接口Canvas屏幕,和Image、Graphics類來繪制游戲面板、棋子,以及游戲的最終結果狀態。要獲取更詳細的信息,請參閱各種繪畫方法和drawCircle、drawCross、drawPiece、drawPlayerCursor、drawBoard等方法。這個屏幕使用MIDlet的quit回調方法來指示游戲結束。
          此屏幕可適應各種可用顯示性能(高、寬、色彩等)。此外還要注意到可以使用四向導航鍵,也可以使用雙向導航鍵來移動光標。

            它使用了封裝了主游戲程序邏輯的Game類。

          package example.tictactoe;
          import java.util.Random;
          import javax.microedition.midlet.*;
          import javax.microedition.lcdui.*;
          class GameScreen extends Canvas implements CommandListener {
          private static final int BLACK = 0x00000000;
          private static final int WHITE = 0x00FFFFFF;
          private static final int RED = 0x00FF0000;
          private static final int BLUE = 0x000000FF;
          private static final int NO_MOVE = -1;
          private final TicTacToeMIDlet midlet;
          private final Game game;
          private final Command exitCommand;
          private final Command newGameCommand;
          private final Random random = new Random();
          private int screenWidth, screenHeight;
          private int boardCellSize, boardSize, boardTop, boardLeft;
          private boolean playerIsCircle;
          private boolean computerIsCircle;
          private int preCursorPosition, cursorPosition;
          private int computerMove = NO_MOVE;
          private int playerMove = NO_MOVE;
          private int computerGamesWonTally = 0;
          private int playerGamesWonTally = 0;
          private boolean isRestart;
          public GameScreen(TicTacToeMIDlet midlet, boolean playerIsCircle) {
          this.midlet = midlet;
          this.playerIsCircle = playerIsCircle;
          computerIsCircle = !playerIsCircle;
          game = new Game(random);
          initializeBoard();
          // configure Screen commands
          exitCommand = new Command("Exit", Command.EXIT, 1);
          newGameCommand = new Command("New", Command.SCREEN, 2);
          addCommand(exitCommand);
          addCommand(newGameCommand);
          setCommandListener(this);
          // begin the game play initialize();
          }
          // Initialize the Game and Game screen. Also used for game restarts.
          private void initialize() {
          game.initialize();
          preCursorPosition = cursorPosition = 0;
          playerMove = NO_MOVE;
          boolean computerFirst = ((random.nextInt() & 1) == 0);
          if (computerFirst) {
          computerMove = game.makeComputerMove();
          }
          else
          {
          computerMove = NO_MOVE;
          }
          isRestart = true;
          repaint();
          }
          public void paint(Graphics g) {
          if (game.isGameOver()) {
          paintGameOver(g);
          }
          else {
          paintGame(g);
          }
          }
          private void paintGame(Graphics g) {
          if (isRestart) {
          // clean the canvas
          g.setColor(WHITE);
          g.fillRect(0, 0, screenWidth, screenHeight);
          drawBoard(g);
          isRestart = false;
          }
          drawCursor(g);
          if (playerMove != NO_MOVE) {
          drawPiece(g, playerIsCircle, playerMove);
          }
          if (computerMove != NO_MOVE) {
          drawPiece(g, computerIsCircle, computerMove);
          }
          }
          private void paintGameOver(Graphics g)

          {
          String statusMsg = null;
          if(game.isComputerWinner()) {
          statusMsg = "I win !";
          computerGamesWonTally++;
          }
          else if (game.isPlayerWinner()) {
          statusMsg = "You win";
          playerGamesWonTally++;
          }
          else {
          statusMsg = "Stalemate";
          }
          String tallyMsg = "You:" + playerGamesWonTally + " Me:" + computerGamesWonTally;
          Font font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
          int strHeight = font.getHeight();
          int statusMsgWidth = font.stringWidth(statusMsg);
          int tallyMsgWidth = font.stringWidth(tallyMsg);
          int strWidth = tallyMsgWidth;
          if (statusMsgWidth > tallyMsgWidth)
          {
          strWidth = statusMsgWidth;
          }
          // Get the
          {
          x, y
          }
          position for painting the strings. int x = (screenWidth - strWidth) / 2;
          x = x < 0 ? 0 : x;
          int y = (screenHeight - 2 * strHeight) / 2;
          y = y < 0 ? 0 : y;
          // clean the canvas
          g.setColor(WHITE);
          g.fillRect(0, 0, screenWidth, screenHeight);
          // paint the strings' text
          g.setColor(BLACK);
          g.drawString(statusMsg, x, y, (Graphics.TOP | Graphics.LEFT));
          g.drawString(tallyMsg, x, (y + 1 + strHeight), (Graphics.TOP | Graphics.LEFT));
          }


          public void commandAction(Command c, Displayable d) {
          if (c == exitCommand) {
          midlet.quit();
          }
          else if (c == newGameCommand) {
          initialize();
          }
          }
          private void initializeBoard() {
          screenWidth = getWidth();
          screenHeight = getHeight();
          if (screenWidth > screenHeight) {
          boardCellSize = (screenHeight - 2) / 3;
          boardLeft = (screenWidth - (boardCellSize * 3)) / 2;
          boardTop = 1;
          }
          else {
          boardCellSize = (screenWidth - 2) / 3;
          boardLeft = 1;
          boardTop = (screenHeight - boardCellSize * 3) / 2;
          }
          }
          protected void keyPressed(int keyCode) {
          // can't continue playing until the player restarts
          if (game.isGameOver()) {
          return;
          }
          int gameAction = getGameAction(keyCode);
          switch (gameAction) {
          case FIRE: doPlayerMove();


          break;
          case RIGHT: doMoveCursor(1, 0);
          break;
          case DOWN: doMoveCursor(0, 1);
          break;
          case LEFT: doMoveCursor(-1, 0);
          break;
          case UP: doMoveCursor(0, -1);
          break;
          default: break;
          }
          }
          private void doPlayerMove() {
          if (game.isFree(cursorPosition)) {
          // player move game.
          makePlayerMove(cursorPosition);
          playerMove = cursorPosition;
          // computer move
          if (!game.isGameOver()) {
          computerMove = game.makeComputerMove();
          }
          repaint();
          }
          }
          private void doMoveCursor(int dx, int dy) {
          int newCursorPosition = cursorPosition + dx + 3 * dy;
          if ((newCursorPosition >= 0) && (newCursorPosition < 9))

          {
          preCursorPosition = cursorPosition;
          cursorPosition = newCursorPosition;
          repaint();
          }
          }
          // Draw a CIRCLE or CROSS piece on the board
          private void drawPiece(Graphics g, boolean isCircle, int pos) {
          int x = ((pos % 3) * boardCellSize) + 3;
          int y = ((pos / 3) * boardCellSize) + 3;
          if (isCircle) {
          drawCircle(g, x, y);
          }
          else {
          drawCross(g, x, y);
          }
          }
          // Draw blue CIRCLE onto the board image
          private void drawCircle(Graphics g, int x, int y) {
          g.setColor(BLUE);
          g.fillArc(x + boardLeft, y + boardTop, boardCellSize - 4, boardCellSize - 4, 0, 360);
          g.setColor(WHITE);
          g.fillArc(x + 4 + boardLeft, y + 4 + boardTop, boardCellSize - 4 - 8, boardCellSize - 4 - 8, 0, 360);
          }
          // Draw red CROSS onto the board image
          private void drawCross(Graphics g, int x, int y) {
          g.setColor(RED);
          for (int i = 0;
          i < 4;
          i++) {
          g.drawLine(x + 1 + i + boardLeft, y + boardTop, x + boardCellSize - 4 - 4 + i + boardLeft, y + boardCellSize - 5 + boardTop);


          g.drawLine(x + 1 + i + boardLeft, y + boardCellSize - 5 + boardTop, x + boardCellSize - 4 - 4 + i + boardLeft, y + boardTop);
          }
          }
          // Visually indicates a Player selected square on the board image
          private void drawCursor(Graphics g) {
          // draw cursor at selected Player square.
          g.setColor(WHITE);
          g.drawRect(((preCursorPosition % 3) * boardCellSize) + 2 + boardLeft, ((preCursorPosition/3) * boardCellSize) + 2 + boardTop, boardCellSize - 3, boardCellSize - 3);
          // draw cursor at selected Player square.
          g.setColor(BLACK);
          g.drawRect(((cursorPosition % 3) * boardCellSize) + 2 + boardLeft, ((cursorPosition/3) * boardCellSize) + 2 + boardTop, boardCellSize - 3, boardCellSize - 3);
          }
          private void drawBoard(Graphics g) {
          // clean the board
          g.setColor(WHITE);
          g.fillRect(0, 0, screenWidth, screenHeight);
          // draw the board
          g.setColor(BLACK);
          for (int i = 0;
          i < 4;
          i++) {
          g.fillRect(boardLeft, boardCellSize * i + boardTop, (boardCellSize * 3) + 2, 2);
          g.fillRect(boardCellSize * i + boardLeft, boardTop, 2, boardCellSize * 3);
          }
          }
          }

            6、Game.java

            這個類封裝了九宮格游戲的主要的游戲程序邏輯。前面我們也說過,游戲程序邏輯本身并不在本例程重點討論的范圍之內,本文主要是介紹MIDP圖形編程的基礎知識。游戲程序邏輯的WINS數組部分來自http://java.sun.com/applets/jdk/1.0/demo/TicTacToe/TicTacToe.java 這個經典例程。

            注意游戲程序邏輯是獨立于游戲用戶界面的(參見類GameScreen),并且可以使用其它實現方法替代。

          package example.tictactoe;
          import java.util.Random;
          import javax.microedition.midlet.*;
          import javax.microedition.lcdui.*;
          // The game logic for TicTacToe
          class Game {
          private static final int[] WINS = {
          // horizontals
          bit(0) | bit(1) | bit(2),
          bit(3) | bit(4) | bit(5),
          bit(6) | bit(7) | bit(8),
          // verticals
          bit(0) | bit(3) | bit(6),
          bit(1) | bit(4) | bit(7),
          bit(2) | bit(5) | bit(8),
          // diagonals
          bit(0) | bit(4) | bit(8),
          bit(2) | bit(4) | bit(6) }
          ;
          private static final int DRAWN_GAME = bit(0) | bit(1) | bit(2) | bit(3) | bit(4) | bit(5) | bit(6) | bit(7) | bit(8);
          private int playerState;
          private int computerState;
          private Random random;
          Game(Random random) {
          this.random = random;
          initialize();
          }
          void initialize() {
          playerState = 0;
          computerState = 0;
          }
          boolean isFree(int position) {
          int bit = bit(position);
          return (((playerState & bit) == 0) && ((computerState & bit) == 0));
          }
          // The 'Contract' is that caller will always make valid moves.
          // We don't check that it's the player's turn.
          void makePlayerMove(int position) {
          playerState |= bit(position);
          }
          // The 'Contract' is that we will be called only when there is still
          // at least one free square.
          int makeComputerMove() {
          int move = getWinningComputerMove();
          if (move == -1) {
          // can't win
          move = getRequiredBlockingComputerMove();
          if (move == -1) {
          // don't need to block
          move = getRandomComputerMove();
          }
          }
          computerState |= bit(move);
          return move;
          }


          boolean isGameOver() {
          return isPlayerWinner() | isComputerWinner() | isGameDrawn();
          }
          boolean isPlayerWinner() {
          return isWin(playerState);
          }
          boolean isComputerWinner() {
          return isWin(computerState);
          }
          boolean isGameDrawn() {
          return (playerState | computerState) == DRAWN_GAME;
          }
          // Return a winning move if there is at least one, otherwise return -1
          private int getWinningComputerMove() {
          int move = -1;
          for (int i = 0;
          i < 9;
          ++i) {
          if (isFree(i) && isWin(computerState | bit(i))) {
          move = i;
          break;
          }
          }
          return move;
          }
          // Return a required blocking move if there is at least one (more
          // than one and we've inevitably lost), otherwise return -1
          private int getRequiredBlockingComputerMove() {
          int move = -1;
          for (int i = 0;
          i < 9;
          ++i) {


          if (isFree(i) && isWin(playerState | bit(i))) {
          move = i;
          break;
          }
          }
          return move;
          }
          // Return a random move in a free square, // or return -1 if none are available private int getRandomComputerMove() {
          int move = -1;
          // determine how many possible moves there are int numFreeSquares = 0;
          for (int i = 0;
          i < 9;
          ++i) {
          if (isFree(i)) {
          numFreeSquares++;
          }
          }
          // if there is at least one possible move, pick randomly
          if (numFreeSquares > 0) {
          // shift twice to get rid of sign bit, then modulo numFreeSquares
          int pick = ((random.nextInt()<<1)>>>1) % numFreeSquares;
          // now find the chosen free square by counting pick down to zero
          for (int i = 0;
          i < 9;
          ++i) {
          if (isFree(i)) {
          if (pick == 0) {
          move = i;
          break;
          }
          pick--;
          }
          }
          }

          return move;
          }
          private static boolean isWin(int state) {
          boolean isWinner = false;
          for (int i = 0;
          i < WINS.length;
          ++i) {
          if ((state & WINS[i]) == WINS[i]) {
          isWinner = true;
          break;
          }
          }
          return isWinner;
          }
          private static int bit(int i) {
          return 1 << i;
          }
          }

            7、TicTacToe.jad

            下面是九宮格MIDlet的應用程序描述文件。

          MIDlet-Name: TicTacToe
          MIDlet-Vendor: Forum Nokia MIDlet-Version: 1.1.1
          MIDlet-Jar-Size: 11409
          MIDlet-Jar-URL: TicTacToe.jar
          MIDlet-1: TicTacToe, /tictactoe.png, example.tictactoe.TicTacToeMIDlet

          posted on 2005-02-16 22:04 藍色雪焰 閱讀(217) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
           
          主站蜘蛛池模板: 麻城市| 云龙县| 灯塔市| 阿荣旗| 鄂州市| 吉安县| 宁都县| 六枝特区| 东兰县| 游戏| 嘉禾县| 唐海县| 响水县| 淅川县| 佛山市| 循化| 乐都县| 绥中县| 普洱| 吐鲁番市| 文水县| 福海县| 安龙县| 南雄市| 江津市| 阿克陶县| 东明县| 江都市| 三穗县| 高密市| 娱乐| 文成县| 绥德县| 海阳市| 华亭县| 内乡县| 英山县| 武平县| 疏附县| 北流市| 扶绥县|