បង្កើត​ Game គ្រួ័រ

បង្កើត​ Game Tic Tac Toe ជាមួយកម្មវិធី Java
1Tic Tac Toe គឺ​ជា​ល្បែង​កំសាន្ត​មួយ​ដ៏ពេញ​និយម ដែល​ក្រឡា​លេង​មាន​៩​ ចន្លោះ​នៅ​លើ​ក្រដាស។ មាន​មនុស្ស​ពីរ​អ្នក​លេង, ម្នាក់​ប្រើ​សញ្ញា​ O, និង​ម្នាក់ទៀត​ប្រើសញ្ញា​ X, គួស​តាម​សញ្ញា​របស់​ខ្លួន​ម្ដង​ម្នាក់ៗតាម​វេន​របស់​ខ្លួន។ អ្នកឈ្នះ​​គឺ ជា​អ្នក​ដែល​ដាក់សញ្ញាឬ​គួស​សញ្ញា​របស់​ខ្លួន​បាន​មុន​ចំនួន​៣តាម ទិស​ឈរ , ដេក ឬខ្វែង។ ហើយ​ប្រសិន​បើ​ក្រោយ​ពី​បាន​ដាក់​ពេញ​ចន្លោះ​ក្រឡា​អស់​ហើយ​នៅ​តែ​មិន​មែន​អ្នក​ឈ្នះឬ​អ្នក​ចាញ់​នោះ គឺ​មាន​ន័យ​ថា​ ស្មើ​គ្នា។ សូម​ក្រឡេក​មើលរូប​គំរូ​ចំនួន​៣​ខាង​ក្រោម​នេះ​:
1(a) អ្នក​កាន់ X ឈ្នះ                 ​​​​​​​​​​​​​​                      (b) ស្មើគ្នា                                                        (c) អ្នក​កាន់

ក្នុង​អត្ថបទ​នេះ នឹង​ឧទ្ទេស​នាមអ្នក​ពី​ដំណោះ​ស្រាយដើម្បីរចនា​នឹង​កសាង​នូវ​កម្ម​វិធី​កំសាន្ត​មួយ​នេះ​ដោយ​ប្រើ​ភាសា​ Java។ នេះ​គឺ​ជា​ដំនោះ​ស្រាយ​ដែល​បាន​ឧទ្ទេស​នាម​ដោយ​ Daniel Liang ក្នុង​សៀវ​ភៅ “Introduction to Java Programming“។

វិភាគ​និង​រចនា​ បណ្ដា​រូប​ឧទាហរណ៏​ដែល​អ្នក​បាន​ឃើញ​ខាង​លើ​គឺ​គ្រាន់​តែ​ជា​ករណី​ខ្លះ​នៃ​ល្បែង​កំសាន្ត​នេះ​ដែល​យើង​យក​មក​បង្ហាញនូវ​សកម្ម​ភាព ​ឈ្នះ​ចាញ់​។តា​ម​ពិត​ទៅ ល្បែង​កំសាន្ត​ Tic Tac Toe នៅ​មាន​ចំនុច​ពិសេសៗនិង​ស្មុគ្រ​ស្មាញ​ច្រើនជាង​នេះ​​​ទៀត ដើម្បី​បង្កើត​បាន​ Class សម្រាប់​ធ្វើ​បទ បង្ហាញលើ​សកម្មភាព​ឈ្នះ​ចាញ់នេះ , យើង​ត្រូវ​ស្រាវ​ជ្រាវ និង​យល់អោយ​ច្បាស់​ជាង​នេះ អំពីល្បែង​កំសាន្ត​មួយ​នេះ ។
Web IT2 copyឧបមា​ថា​ដំបូង​ឡើយ​គឺ មាន​ក្រឡា​ទាំង​អស់ (cell) ទំនេរ​(មិន​ទាន់គួស), ហើយ​អ្នក​លេង​ទី​១​គឺ​ប្រើ​សញ្ញាX,អ្នក​លេង​ទី​២​គឺ​ប្រើ​សញ្ញា​ O។ ដើម្បី​ អនុវត្ត​ជំហាន​ដើរ​ នោះ​អ្នក​លេង​ រំកិល Cursor ដល់​ក្រឡាណា​មួយ​រួច​ចុច​នៅទីនេះ ។,បើ​សិន​ជា​ក្រឡា​ណា​មួយ​​ O (ឬ X)មិន​ទាន់​គួស​ នោះ​អ្នក​ទី២នឹង​ត្រូវគួស​បន្ត,​ហើយ​ផ្ទុ​យ​មក​វិញ​បើ​ក្រឡា​នោះ​មាន​គេ​គួស​ហើយ​គឺ​មិន​អនុញ្ញាតិ​អោយ​គួស​លើ​ទៀត​ឡើយ។ តាម​រយៈ​ការ​ពណ៌នា​ខាង​លើ​ យើង​ឃើញ​ថា​ ចំពោះ​ក្រឡា​(Cell)នីមួយ​ គឺ​ជា​ Object GUI (Graphic User Interface –

ផ្ទៃ​ប្រសព្វ​អ្នក​ប្រើ)មួយ​ដែល​អាច​​សំរួល​ការ​ងារ​ពេល​មាន​ការ​ចុច​បញ្ជាលើវា​និង​បង្ហាញសញ្ញា​ដែល​បាន​កំណត់​ណាមួយ។ Object នេះ​អាច ជា​ ប៊ូតុង(button) ឬ​ជា​ផ្ទាំង មួយ (panel)។ ការ​គួសចេញ​​លើ panelមួយ​គឺ​វា​នឹង​មាន​ភាព​រស់​រវើក​ជាង បើ​ធ្វើ​នឹង​ការ​គួស​លើbutton, ដោយ​ហេតុ​ថា​នៅ​លើ​ panelមួយ​គឺ​យើង​អាច​គួសបណ្ដាស​ញ្ញា X និង O ជាមួយ​ទំហំ​ណា​មួយ​ក៏​បាន​ ដែល​យើ​ង​បាន​​, ហើយ​ចំណែក​នៅ​លើ​ button មួយ​គឺ​យើង​អាច​បង្ហាញ​​ចេញ​តែ​ចំពោះ​អក្សរឈ្មោ (text label)។ ដោយ​ហេតុ​​ផល​នេះ​យើង​នឹង​ប្រើ​ប្រាស់​ panel មួយ​ដើម្បី​ធ្វើ​បទ​បង្ហាញ​ ក្រឡា​មួយ​របស់​ក្ដា​លេងឬ​ក្រដាស​លេង។ ធ្វើ​របៀ​ប​ណា​​យើង​អាច​ដឹងសញ្ញាបច្ចុប្បន្ន​របស់​ក្រឡានីមួយ​ៗ​តើ​តើ​វាជា (ទំនេរ, X, ឬ O)? នៅ​ក្នុង​ Class Cell យើង​នឹង​ប្រើ​លក្ខណៈ​សម្គាល់​មួយ​(property)ឈ្មោះថា​ token មាន​ប្រភេទ​ទិន្ន័យ​ជា​ char។ Class Cell មាន​ភារកិច្ចគួសសញ្ញាពេលក្រឡា​ទំនេរពេល​ចុច Mouse (Mouse is clickede), ដូច្នេះ​​យើង​ត្រូវ​ការ​សរសេរកូដ​មួយ​ដើម្បី​រង​ចាំ​ស្ដាប់ព្រឹត្តការ​ ( Event Listener) ឈ្មោះថា MouseEvent និង​កំណាត់​កូដ​មួយ​ផ្សេង​ទៀត​ដើម្បី​គួស បណ្ដាញ​សញ្ញា​ X ឬ O។ Class Cell អាច​កំនត់ន័យបាន​ដូច​រូប​ភាព​បង្ហាញខាង​ក្រោម​នេះ :
1Class Cell គួស​សញ្ញា​ក្នុង​ចន្លោះ​​ក្រឡា
token: សម្រាប់សំគាល់​ក្នុង​ក្រឡា, តម្លៃ​កំណត់ស្រាប់គឺ ”
– getToken(): ផ្ដល់​តំលៃ​អោយ​ដឹង​ សញ្ញា​ដែល​កំពុង​​មាន​លើ​ចន្លោះ​ក្រឡា​.
– setToken(token: char): ដាក់សញ្ញា​ថ្មី​អោយ​ចន្លោះ​ក្រឡា​.
– paintComponent(g: Graphics): គូស​សញ្ញា​​នៅ​ចន្លោះ​ក្រឡា
– mouseClicked(e: MouseEvent): សម្រួលការ​ងារ​ពេល​​មាន​ព្រឹត្តិការ​ចុចលើ​ចន្លោះ​ក្រឡា​នោះ ។ក្ដា​លេង​​ល្បែង​កំសាន្ត​ Tic Tac Toe ​រួម​មាន​ 9 ចន្លោះ​, បង្កើត​ឡើង​ដោយ​ឃ្លា​បញ្ជា new Cell[3][3]. ដើម្បី​កំណត់វេន​ដើរ​ជា​បន្ត​បន្ទាប់​របស់​អ្នក​លេង​​ យើង​នឹង​ប្រើអថេរ​មួយ​ឈ្មោះថា​ whoseTurn (វេននរណា​ដើរ) ជាមួយ​ប្រភេទ ទិន្ន័យ char. ដំបូង​ឡើយ​ whoseTurn នឹង​មាន​តម្លៃ ‘X’,ក្រោយ​មក​ប្ដូរជា​ ‘O’ និង​គឺ​បន្ត​បន្ទាប់​ផ្លាស់​ប្ដូរ​វេន រវាង​តម្លៃ​ទាំង​ពីរ​គ្រប់​ពេល​ណា​ដែល​ចន្លោះ​ក្រឡា​ណា​ដែល​បាន​គួស។ ពេល​ដែល​ការ​ប្រកួត​ចប់​នោះ​តម្លៃ​របស់ whoseTurn នឹង​ប្ដូរ​មក​ជា ‘ ‘.
ធ្វើ​យ៉ាង​ណា​ដើម្បី​ដឹង​ថា​ ល្បែងកំសាន្ត​​ត្រូវ​បាន​បញ្ចប់​ហើយ​ឬ​នៅ ? តើ​មាន​នរណា​ឈ្នះ​ហើយ​ឬ​នៅ? ​ហើយ​នរណា​ជា​អ្នក​ឈ្នះ​ បើ​មាន ? យើង​អាច​បង្កើត​បាន​ Method មួយ​ឈ្មោះ​ថា​ isWon (char token) ដើម្បី​ពិនិត្យ​មើល​ថា​ អ្នកលេង​ដែល​មាន​ការ​កំណត់សំគាល់ token បាន​ទទួល​ជ័យ​ជំនះ​ហើយ​ឬ​នៅ , និង Method មួយ​ទៀត គឺ isFull() ដើម្បី​ពិនិត្យ​មើល​ថា​ តើ​ក្រឡា​ទាំង​អស់​ត្រូវ​បាន​គួស​អស់​ឬ​នៅ ។ ជាមួយ​និង​ការ​វិភាគ​ដូច្នេះ, យើង​ឃើញ​ថា​ចំា​បាច់​ត្រូវ​ការ 2 Classes . Class ដំបូង​គឺ Cell, ជាមួយ​តួនាទី របស់​វា​គឺ សំរួល​ការ​ងា​ដែល​កើត​មាន​លើ​ក្រឡាង​ណា​មួយ​។ Class ទី​ពីរ​គឺ​ TicTacToe មាន​មុខ​ងា​រ​និងតួនាទី អនុវត្ត​និង​សម្រួល​ការ​ងារ​ទាំង​ស្រុង​លើ បណ្ដា​​ក្រឡា។ ទំនាក់ទំនង​ Class ទាំង​ពីរ​នេះ​អាច​បង្ហាញចេញ​ជា​រូប​ភាព​ខាង​ក្រោម​នេះ :
1– whoseTurn: ចង្អុល​បញ្ជាក់​ពី វេន​ដើរ​របស់​អ្នក​ដើរ, ដំបូង​គឺ X.
– cell: Array មានទិស ទំហំ 3 x 3 ក្រឡា.
– jlblStatus: ស្លាកមួយ (label) ដើម្បី​បង្ហាញ​ស្ថាន​ភាព​របស់ game.
– TicTacToe(): បង្កើតឡើង GUI
– isFull(): អោយ​តំលៃ ជា true បើ​ក្រឡា​ទាំង​អស់​ត្រូវ​បាន​គួស.
– isWon(token: char): អោយ​តំលៃ ជា  true ​បើ​សិន​អ្នក​លេង​មាន​សញ្ញា token បាន​ឈ្នះ.
ដោយ ​ហេតុ​ថា​ Class Cell ប្រើ​បាន​តែ​ក្នុង Class TicTacToe ហេតុនេះ​វា​អាច កំនត់អត្ថន័យ​ខាង​ក្នុង​ Class TicTacToe (ហៅ​ថា​ inner class).
កូដ​លំអិត​របស់​កម្ម​វិធី​ សសរសេរ​ដូច​ខាង​ក្រោម​នេះ :

001
002
import java.awt.*;
import java.awt.event.*;
003
import javax.swing.*;
004
import javax.swing.border.LineBorder;
005
006
public class TicTacToe extends JApplet {
007
  // កំណត់វេនបន្ត​បន្ទាប់​របស់​អ្នក​លេង​, តម្លៃ​ដំបូង​គឺ X
008
  private char whoseTurn = 'X';
009
010
  // បង្កើត​ក្រឡា​ឡើង
011
  private Cell[][] cells =  new Cell[3][3];
012
013
  // បង្កើត​ label កំណត់​ស្ថានភាព​របស់​ល្បែង​កំសាន្ត
014
  private JLabel jlblStatus = new JLabel(<!--DVFMTSC-->"X's turn to play<!--DVFMTSC-->");
015
016
  /** Initialize UI */
017
  public TicTacToe() {
018
    // Panel p សម្រាប់​ដាក់បណ្ដា​ក្រឡា​
019
    JPanel p = new JPanel(new GridLayout(3, 3, 0, 0));
020
    for (int i = 0; i &lt; 3; i++)
021
      for (int j = 0; j &lt; 3; j++)
022
        p.add(cells[i][j] = new Cell());
023
024
    // ផ្លាស់​ប្ដូរ​ខ្សែ​ព្រំដែន​របស់​ក្រឡា និង​label សណ្ឋាន​ភាព
025
    p.setBorder(new LineBorder(Color.red, 1));
026
    jlblStatus.setBorder(new LineBorder(Color.yellow, 1));
027
028
    // បញ្ចូន&nbsp;panel និង​&nbsp;label ទៅ​ក្នុង​&nbsp;applet
029
    add(p, BorderLayout.CENTER);
030
    add(jlblStatus, BorderLayout.SOUTH);
031
  }
032
033
  /**
034
    កំណត់​មើល​ថា​ តើ​ក្រឡា​ទាំង​អស់​ត្រូវ​បាន​ គួស​ហើយ​ ឬ​នៅ&nbsp;
035
  */
036
  public boolean isFull() {
037
    for (int i = 0; i &lt; 3; i++)
038
      for (int j = 0; j &lt; 3; j++)
039
        if (cells[i][j].getToken() == ' ')
040
          return false;
041
042
    return true;
043
  }
044
045
  /**
046
   &nbsp;កំណត់​មើល​ថាតើ​អ្នក​លេង​ដែល​កាន់យក​សញ្ញា&nbsp;token បាន​ឈ្នះ ហើយ​ ឬ​នៅ&nbsp;
047
  */
048
  public boolean isWon(char token) {
049
    for (int i = 0; i &lt; 3; i++)
050
      if ((cells[i][0].getToken() == token)
051
          &amp;&amp; (cells[i][1].getToken() == token)
052
          &amp;&amp; (cells[i][2].getToken() == token)) {
053
        return true;
054
      }
055
056
    for (int j = 0; j &lt; 3; j++)
057
      if ((cells[0][j].getToken() ==  token)
058
          &amp;&amp; (cells[1][j].getToken() == token)
059
          &amp;&amp; (cells[2][j].getToken() == token)) {
060
        return true;
061
      }
062
063
    if ((cells[0][0].getToken() == token)
064
        &amp;&amp; (cells[1][1].getToken() == token)
065
        &amp;&amp; (cells[2][2].getToken() == token)) {
066
      return true;
067
    }
068
069
    if ((cells[0][2].getToken() == token)
070
        &amp;&amp; (cells[1][1].getToken() == token)
071
        &amp;&amp; (cells[2][0].getToken() == token)) {
072
      return true;
073
    }
074
075
    return false;
076
  }
077
078
  // inner class មួយ តំណាង​អោយ ក្រឡា​មួយ​ចន្លោះ&nbsp;
079
  public class Cell extends JPanel {
080
    // សញ្ញា​សម្គាល់​របស់​ចន្លោះ ក្រឡា​នេះ&nbsp;
081
    private char token = ' ';
082
083
    public Cell() {
084
      setBorder(new LineBorder(Color.black, 1)); // ផ្លាស់​ប្ដូរ​ខ្សែ​ព្រំ​ដែន​ក្រឡា
085
      addMouseListener(new MyMouseListener());  // ចុះ​ឈ្មោះ&nbsp;listener
086
    }
087
088
    /**
089
        អោយ​តំលៃ​ជា​សញ្ញា​សំគាល់​របស់​ក្រឡា​
090
    */
091
    public char getToken() {
092
      return token;
093
    }
094
095
    /**
096
        បញ្ចូល​សញ្ញា​ថ្មី​អោយ​ក្រឡា
097
    */
098
    public void setToken(char c) {
099
      token = c;
100
      repaint();
101
    }
102
103
    /**
104
        គួស​ក្រឡា​
105
&nbsp;   */
106
    protected void paintComponent(Graphics g) {
107
      super.paintComponent(g);
108
109
      if (token == 'X') {
110
        g.drawLine(10, 10, getWidth() - 10, getHeight() - 10);
111
        g.drawLine(getWidth() - 10, 10, 10, getHeight() - 10);
112
      }
113
      else if (token == 'O') {
114
        g.drawOval(10, 10, getWidth() - 20, getHeight() - 20);
115
      }
116
    }
117
118
    private class MyMouseListener extends MouseAdapter {
119
      /** សំរួល​ការ​ងារ​ពេល​មាន​ការ​ចុះ​ Mouse ក្នុង ក្រឡា&nbsp;*/
120
      public void mouseClicked(MouseEvent e) {
121
        // បើ​សិន​ជា​ក្រឡា​នៅ​ទំនេរ​ ហើយ​ល្បែង​កំសាន្ត​នៅ​មិន​ទាន់​បញ្ចប់
122
        if (token == ' ' &amp;&amp; whoseTurn != ' ') {
123
          setToken(whoseTurn); // ផ្លាស់​ប្ដូរ​សញ្ញា​អោយ​ក្រឡា
124
          // ពិនិត្យ​មើល​សណ្ឋាភាព​របស់​ល្បែង​កំសាន្ត​
125
          if (isWon(whoseTurn)) {
126
            jlblStatus.setText(whoseTurn + "&nbsp;won! The game is over");
127
            whoseTurn = ' '; // ល្បែង​កំសាន្ត​ត្រូវ​បាន​បញ្ចាប់
128
          }
129
          else if (isFull()) {
130
            jlblStatus.setText(<!--DVFMTSC-->"Draw! The game is over<!--DVFMTSC-->");
131
            whoseTurn = ' '; // ល្បែង​កំសាន្ត​ត្រូវ​បាន​បញ្ចាប់
132
          }
133
          else {
134
            // ផ្លាស់​ប្ដូរ​វេន​ដើរ
135
            whoseTurn = (whoseTurn == 'X') ? 'O': 'X';
136
            // បង្ហាញ​ វេន​អ្នក​ដើរ
137
            jlblStatus.setText(whoseTurn + <!--DVFMTSC-->"'s turn<!--DVFMTSC-->");
138
          }
139
        }
140
      }
141
    }
142
  }
143
144
  /**
145
    Method &nbsp;main() អនុញ្ញាតិ​អោយ​&nbsp;applet រត់​បាន​ដូច​ទៅ​នឹង​កម្ម​វិធី​ប្រើ​ប្រាស់​មួយ​អច្ចឹង​
146
  */
147
  public static void main(String[] args) {
148
    // បង្កើត&nbsp;frame មួយ
149
    JFrame frame = new JFrame(<!--DVFMTSC-->"TicTacToe";);
150
151
    // បង្កើត instance (តំណាង)មួយរបស់&nbsp;applet
152
    TicTacToe applet = new TicTacToe();
153
154
    // បន្ថែម​ instance របស់&nbsp;applet ទៅ​ក្នុង​&nbsp;frame
155
    frame.add(applet, BorderLayout.CENTER);
156
157
    // បង្ហាញ​&nbsp;frame
158
    frame.setSize(300, 300);
159
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
160
    frame.setVisible(true);
161
  }
162
}