ผลต่างระหว่างรุ่นของ "Oop lab/sokoban"

จาก Theory Wiki
ไปยังการนำทาง ไปยังการค้นหา
แถว 23: แถว 23:
 
     private int width;
 
     private int width;
 
     private String[] baseBoard;
 
     private String[] baseBoard;
 +
   
 
     private int playerRow;
 
     private int playerRow;
 
     private int playerCol;
 
     private int playerCol;
 +
   
 
     private int numBoxes;
 
     private int numBoxes;
     private int[][] boxPositions;
+
     private int[] boxRows;
 +
    private int[] boxCols;
  
 
     public GameBoard(String[] map) {
 
     public GameBoard(String[] map) {
แถว 36: แถว 39:
 
         width = map[0].length();
 
         width = map[0].length();
 
         numBoxes = 0;
 
         numBoxes = 0;
         boxPositions = new int[height*width][2];
+
         boxRows = new int[height*width];
 +
        boxCols = new int[height*width];
 
          
 
          
 
         baseBoard = new String[height];
 
         baseBoard = new String[height];
แถว 50: แถว 54:
 
                     break;
 
                     break;
 
                 case 'O':
 
                 case 'O':
                     boxPositions[numBoxes][0] = r;
+
                     boxRows[numBoxes] = r;
                     boxPositions[numBoxes][1] = c;
+
                     boxCols[numBoxes] = c;
 
                     numBoxes++;
 
                     numBoxes++;
 
                     break;
 
                     break;
แถว 89: แถว 93:
 
     public int[] getBoxPosition(int i) {
 
     public int[] getBoxPosition(int i) {
 
         return new int[] {  
 
         return new int[] {  
                 boxPositions[i][0],
+
                 boxRows[i],
                 boxPositions[i][1]
+
                 boxCols[i]
 
         };
 
         };
 
     }
 
     }
 
      
 
      
 
     public void setBoxPosition(int i, int r, int c) {
 
     public void setBoxPosition(int i, int r, int c) {
         boxPositions[i][0] = r;
+
         boxRows[i] = r;
         boxPositions[i][1] = c;
+
         boxCols[i] = c;
 
     }
 
     }
  

รุ่นแก้ไขเมื่อ 23:48, 8 กันยายน 2559

หน้านี้เป็นส่วนหนึ่งของ oop lab

เกม Sokoban เป็นเกมปัญหาที่โด่งดังมากในอดีต ทดลองเล่นได้ที่ [1] หรือ [2]

ในเกมนี้ผู้เล่นจะเดินเข็นกล่องไปมาให้ไปอยู่ในตำแหน่งปลายทาง ผู้เล่นจะดันกล่องได้เท่านั้น (ดึงไม่ได้ถ้าดันไปติดกำแพงก็จบเกม) และจะสามารถดันกล่องได้แค่ใบเดียวเท่านั้น (ถ้าติดกันสองใบจะหนักเกิน ดันไม่ไหว)

เริ่มต้น

เราจะพัฒนาเกมดังกล่าวด้วยกระบวนการ TDD และหัดใช้ git ไปพร้อม ๆ กัน ดังนั้นให้สร้างโปรเจ็ค จากนั้นให้สร้าง git repository ของโปรเจ็คด้วย (ดูวิธีการได้ในคลิป egit)

ด้านล่างเป็นคลาส GameBoard และ unit test เบื้องต้น GameBoardTest ให้สร้างคลาสดังกล่าวใน project และนำโค้ดด้านล่างไปใช้ อย่าลืมดูชื่อ package ให้สอดคล้องกับ project ที่สร้างขึ้นด้วย (โดยมากนิยมใช้ชื่อเดียวกัน)

ให้ทดลองสั่งให้ unit test ทำงาน ถ้าเขียวหมด ให้ commit เวอร์ชันนี้ลง git

Gitmark.png ถ้า unit test เขียวหมด ให้ commit สามารถใส่ commit message ว่า initial project template

GameBoard.java

package sokoban;

public class GameBoard {

    private int height;
    private int width;
    private String[] baseBoard;
    
    private int playerRow;
    private int playerCol;
    
    private int numBoxes;
    private int[] boxRows;
    private int[] boxCols;

    public GameBoard(String[] map) {
        loadBoard(map);
    }

    public void loadBoard(String[] map) {
        height = map.length;
        width = map[0].length();
        numBoxes = 0;
        boxRows = new int[height*width];
        boxCols = new int[height*width];
        
        baseBoard = new String[height];
        for(int r = 0; r < height; r++) {
            baseBoard[r] = "";
            for(int c = 0; c < width; c++) {
                char mch = map[r].charAt(c);
                char sch = '.';
                switch(mch) {
                case 'A': 
                    playerRow = r;
                    playerCol = c;
                    break;
                case 'O':
                    boxRows[numBoxes] = r;
                    boxCols[numBoxes] = c;
                    numBoxes++;
                    break;
                default:
                    sch = mch;
                }
                baseBoard[r] += sch;
            }
        }
    }

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }

    public int getPlayerRow() {
        return playerRow;
    }
    
    public int getPlayerCol() {
        return playerCol;
    }
    
    public void setPlayerPosition(int r, int c) {
        playerRow = r;
        playerCol = c;
    }

    public int getNumBoxes() {
        return numBoxes;
    }

    public int[] getBoxPosition(int i) {
        return new int[] { 
                boxRows[i],
                boxCols[i]
        };
    }
    
    public void setBoxPosition(int i, int r, int c) {
        boxRows[i] = r;
        boxCols[i] = c;
    }

    public boolean hasPlayerAt(int r, int c) {
        return (playerRow == r) && (playerCol == c);
    }
    
    public boolean hasBoxAt(int r, int c) {
        return false;
    }
    
    public boolean hasExitAt(int r, int c) {
        return false;
    }
    
    public String toString() {
        return "";
    }
}

GameBoardTest.java

package sokoban;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

public class GameBoardTest {

    static String smallBoardMap[] = {
        " #####",
        "#*O.A#",
        "#...O#",
        "##..*#",
        " #####"
    };
    private GameBoard smallBoard;

    @Before
    public void setUp() {
        smallBoard = new GameBoard(smallBoardMap);
    }
    
    @Test
    public void testLoadBoardProperties() {
        assertEquals(5, smallBoard.getHeight());
        assertEquals(6, smallBoard.getWidth());
    }

    @Test
    public void testLoadBoardPlayerPosition() {
        assertEquals(1, smallBoard.getPlayerRow());
        assertEquals(4, smallBoard.getPlayerCol());
    }
    
    @Test
    public void testLoadBoardNumBoxes() {
        assertEquals(2, smallBoard.getNumBoxes());
    }

    @Test
    public void testLoadBoardBoxPositions() {
        assertArrayEquals(new int[] {1, 2}, smallBoard.getBoxPosition(0));
        assertArrayEquals(new int[] {2, 4}, smallBoard.getBoxPosition(1));
    }    
    
    @Test
    public void testPlayerPositionCheck() {
        assertFalse(smallBoard.hasPlayerAt(0,0));
        assertTrue(smallBoard.hasPlayerAt(1,4));
    }
    
    @Test
    public void testPlayerPositionCheckAfterChange() {
        smallBoard.setPlayerPosition(2, 3);
        assertFalse(smallBoard.hasPlayerAt(1,4));
        assertTrue(smallBoard.hasPlayerAt(2, 3));
    }
}

กระดานเกมทั่วไป และการแสดงผล

คลาส GameBoard มีเมท็อดทั่วไปต่าง ๆ ให้ใช้ในการโหลดตารางเกมและอ่านข้อมูลพื้นฐาน อย่างไรก็ตามยังขาดเมท็อดที่จำเป็นอีกหลายอย่าง ในส่วนนี้เราจะเขียนเมท็อดสำหรับตรวจสอบว่าในช่องที่ระบุมีกล่องอยู่หรือไม่ และในช่องที่ระบุมีทางออกอยู่หรือไม่ จากนั้นเราจะเขียนเมท็อด toString เพื่อเตรียมไว้สำหรับพิมพ์ตารางเกมออกทางหน้าจอ

พิจารณาการเก็บข้อมูลใน GameBoard จากโค้ดตั้งต้น จะมีการเก็บข้อมูลดังนี้

  • ข้อมูลตารางเกมพื้นฐาน (ที่ไม่มีการเปลี่ยนแปลงระหว่างเล่น) จะเก็บในอาร์เรย์ baseBoard ที่เก็บตารางเป็นสตริงทีละแถว สตริงจะประกอบด้วย '#' แทนกำแพง '.' แทนทางเดินในห้อง และ '*' แทนจุดเป้าหมาย (เรียกว่า exit)
  • ตำแหน่งในตารางเกมจะเก็บเป็น row, column โดยเริ่มนับจาก 0 (แถวแรกคือแถวที่ 0, คอลัมน์แรกคือคอลัมน์ที่ 0)
  • ตำแหน่งผู้เล่น เก็บใน playerRow และ playerCol
  • จำนวนกล่อง เก็บใน numBoxes
  • ตำแหน่งของกล่อง เก็บเป็นอาเรย์ boxRows และ boxCols โดยกล่องที่ i อยู่ที่แถว boxRows[i] และคอลัมน์ boxCols[i]

การขยับ

เล่นเกมกัน!

เริ่มต้นใหม่และการถอยหลัง (undo)