Test





بناء لعبة إكس-أو (Tic-Tac-Toe) احترافية في أندرويد ستوديو




بناء لعبة إكس-أو (Tic-Tac-Toe) احترافية في أندرويد ستوديو

أهلاً بك في هذا الدليل التعليمي الشامل حيث ستتعلم كيفية بناء لعبة إكس-أو احترافية باستخدام أندرويد ستوديو ولغة جافا. هذا المشروع مثالي للمبتدئين والمطورين المتوسطين الذين يتطلعون إلى تعزيز مهاراتهم في تطوير ألعاب أندرويد.

مقدمة

ماذا ستتعلم: بنهاية هذا الدليل، ستكون قد أنشأت لعبة إكس-أو كاملة الوظائف مع خصم ذكاء اصطناعي ذكي. ستتعلم كيفية إعداد مشروع أندرويد، وتصميم واجهة مستخدم، وتطبيق منطق اللعبة، وتحسين تطبيقك بميزات متقدمة مثل الرسوم المتحركة والأنماط المخصصة.

المتطلبات الأساسية

  • فهم أساسي للغة البرمجة جافا.
  • معرفة ببيئة التطوير المتكاملة أندرويد ستوديو.
  • تثبيت أندرويد ستوديو على جهازك. تحميل من هنا.
  • تثبيت Java Development Kit (JDK). تحميل من هنا.

1. إعداد المشروع

1.1 إنشاء مشروع جديد

  1. افتح أندرويد ستوديو وانقر على “Start a new Android Studio project”.
  2. اختر “Empty Activity” وانقر على “Next”.

نافذة مشروع جديد في أندرويد ستوديو مع اختيار Empty Activity.

  1. قم بتهيئة مشروعك كما هو موضح في الصورة أدناه:
    • Name: TicTacToePro
    • Package name: com.example.tictactoepro
    • Language: Java
    • Minimum API level: API 21: Android 5.0 (Lollipop)

شاشة تهيئة المشروع مع ملء الاسم، اسم الحزمة، اللغة، والحد الأدنى لواجهة برمجة التطبيقات.

  1. انقر على “Finish” لإنشاء المشروع.

1.2 هيكل المشروع

بعد إنشاء المشروع، يجب أن يكون لديك الهيكل التالي في نافذة المشروع. انتبه إلى موقع ملفي `MainActivity.java` و `activity_main.xml`، حيث سنقوم بتعديلهما.

عرض هيكل المشروع في أندرويد ستوديو يوضح المجلدات والملفات الرئيسية.

  • app/src/main/java/com/example/tictactoepro/MainActivity.java
  • app/src/main/res/layout/activity_main.xml
  • app/src/main/res/values/strings.xml
  • app/src/main/AndroidManifest.xml
نصيحة: حافظ على تنظيم مشروعك باتباع اصطلاحات التسمية القياسية وهياكل المجلدات.

2. تصميم واجهة المستخدم

سنقوم بتصميم واجهة مستخدم نظيفة وبديهية للعبتنا. ستبدو النتيجة النهائية مثل الصورة أدناه، وتتضمن عنوانًا ولوحة نتائج وشبكة اللعب 3×3.

الواجهة النهائية للعبة إكس-أو.

2.1 تعديل ملف activity_main.xml

استبدل محتوى ملف activity_main.xml بالكود التالي لإنشاء التخطيط الموضح أعلاه:




    

    

        

        

    

    

        

            
            
            

            

            

            
            

            

            

            
            

            

            

        
    

    

2.2 تعريف الأنماط في styles.xml

أضف الأنماط التالية إلى ملف styles.xml:



    

    

2.3 إضافة الرسوم المتحركة (Animations)

أنشئ ملفي رسوم متحركة في مجلد res/anim. للقيام بذلك، انقر بزر الماوس الأيمن على مجلد `res`، اختر `New` -> `Android Resource Directory`، واختر `anim` كنوع المورد. ثم، انقر بزر الماوس الأيمن على مجلد `anim` الجديد لإنشاء الملفات.

2.3.1 scale_up.xml


2.3.2 scale_down.xml


ملاحظة: ستنشئ هذه الرسوم المتحركة تأثير النقر على الزر عن طريق تكبيره ثم تصغيره مرة أخرى.

3. كتابة منطق اللعبة

سنقوم الآن بتطبيق منطق اللعبة في `MainActivity.java`. بهذا الكود، تصبح اللعبة قابلة للعب. يمكنك النقر على المربعات، وسيقوم الكمبيوتر بحركته، وستكتشف اللعبة حالات الفوز أو الخسارة أو التعادل.

لعبة إكس-أو قيد التقدم، تظهر علامات X و O على اللوحة.

3.1 تحديث ملف MainActivity.java

استبدل محتوى ملف MainActivity.java بالكود التالي:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button[][] buttons = new Button[3][3];
    private boolean player1Turn = true;
    private int roundCount;
    private int player1Points;
    private int player2Points;
    private TextView textViewPlayer1;
    private TextView textViewPlayer2;
    private String name1, name2;
    private Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textViewPlayer1 = findViewById(R.id.text_view_p1);
        textViewPlayer2 = findViewById(R.id.text_view_p2);

        name1 = "أنت";
        name2 = "الحاسوب";

        textViewPlayer1.setText(String.format("%s: 0", name1));
        textViewPlayer2.setText(String.format("%s: 0", name2));

        // تهيئة الأزرار وربطها بحدث النقر
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                String buttonID = "button_" + i + j;
                int resID = getResources().getIdentifier(buttonID, "id", getPackageName());
                buttons[i][j] = findViewById(resID);
                buttons[i][j].setOnClickListener(this);
            }
        }

        FloatingActionButton buttonReset = findViewById(R.id.button_reset);
        buttonReset.setOnClickListener(v -> resetBoard());
    }

    @Override
    public void onClick(View v) {
        if (!((Button) v).getText().toString().equals("")) {
            return;
        }

        // تطبيق الأنيميشن عند النقر على الزر
        animateButton(v);

        if (player1Turn) {
            ((Button) v).setText("X");
            ((Button) v).setTextColor(getResources().getColor(R.color.yellowX)); // تعيين لون "X"
            roundCount++;
            if (checkForWin()) {
                player1Wins();
                return;
            } else if (roundCount == 9) {
                draw();
                return;
            }
            player1Turn = false;
            computerMove();
        }
    }

    private void computerMove() {
        handler.postDelayed(() -> {
            // محاولة الفوز أولاً
            if (!tryToWin("O")) {
                // إذا لم يكن الفوز ممكنًا، حاول صد اللاعب
                if (!tryToBlock()) {
                    // إذا لم يكن الصد ضروريًا، قم بحركة عشوائية
                    makeRandomMove();
                }
            }
            if (checkForWin()) {
                computerWins();
            } else if (roundCount == 9) {
                draw();
            } else {
                player1Turn = true;
            }
        }, 500); // تأخير 500 مللي ثانية لمحاكاة التفكير
    }

    private boolean tryMove(String playerMark, String actualMark) {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (buttons[i][j].getText().toString().equals("")) {
                    buttons[i][j].setText(playerMark); // ضع العلامة مؤقتًا
                    if (checkForWin()) {
                        buttons[i][j].setText(actualMark); // ضع العلامة الفعلية إذا كانت حركة فائزة
                        if (actualMark.equals("O")) {
                            buttons[i][j].setTextColor(getResources().getColor(R.color.redO)); // تأكد من أن "O" باللون الأحمر
                        } else {
                            buttons[i][j].setTextColor(getResources().getColor(R.color.yellowX)); // تأكد من أن "X" باللون الأصفر
                        }
                        roundCount++;
                        return true;
                    }
                    buttons[i][j].setText(""); // تراجع عن الحركة إذا لم تكن ناجحة
                }
            }
        }
        return false;
    }
    
    private void makeRandomMove() {
        Random random = new Random();
        int row, col;
        do {
            row = random.nextInt(3);
            col = random.nextInt(3);
        } while (!buttons[row][col].getText().toString().equals(""));

        buttons[row][col].setText("O");
        buttons[row][col].setTextColor(getResources().getColor(R.color.redO));
        roundCount++;
    }
    
    private boolean tryToWin(String mark) {
        return tryMove(mark, mark);
    }

    private boolean tryToBlock() {
        return tryMove("X", "O");
    }

    private boolean checkForWin() {
        // التحقق من جميع حالات الفوز للصفوف والأعمدة والأقطار
        for (int i = 0; i < 3; i++) {
            if (checkMatch(buttons[i][0], buttons[i][1], buttons[i][2])) return true;
            if (checkMatch(buttons[0][i], buttons[1][i], buttons[2][i])) return true;
        }
        if (checkMatch(buttons[0][0], buttons[1][1], buttons[2][2])) return true;
        if (checkMatch(buttons[0][2], buttons[1][1], buttons[2][0])) return true;
        return false;
    }

    private boolean checkMatch(Button b1, Button b2, Button b3) {
        return !b1.getText().toString().equals("") && b1.getText().toString().equals(b2.getText().toString()) && b1.getText().toString().equals(b3.getText().toString());
    }

    private void player1Wins() {
        player1Points++;
        Toast.makeText(this, name1 + " فاز!", Toast.LENGTH_SHORT).show();
        updatePointsText();
        delayResetBoard();
    }

    private void computerWins() {
        player2Points++;
        Toast.makeText(this, "الحاسوب فاز!", Toast.LENGTH_SHORT).show();
        updatePointsText();
        delayResetBoard();
    }

    private void draw() {
        Toast.makeText(this, "تعادل!", Toast.LENGTH_SHORT).show();
        delayResetBoard();
    }

    private void updatePointsText() {
        textViewPlayer1.setText(String.format("%s: %d", name1, player1Points));
        textViewPlayer2.setText(String.format("%s: %d", name2, player2Points));
    }

    private void delayResetBoard() {
        handler.postDelayed(this::resetBoard, 1500); // تأخير إعادة تعيين اللوحة لمدة 1.5 ثانية
    }

    private void resetBoard() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                buttons[i][j].setText("");
            }
        }
        roundCount = 0;
        player1Turn = true;
    }

    private void animateButton(View view) {
        Animation scaleUp = AnimationUtils.loadAnimation(this, R.anim.scale_up);
        Animation scaleDown = AnimationUtils.loadAnimation(this, R.anim.scale_down);
        view.startAnimation(scaleUp);
        view.startAnimation(scaleDown);
    }

}

ملاحظة: هذا الكود يتضمن منطق الذكاء الاصطناعي لخصم الكمبيوتر، والرسوم المتحركة لنقرات الأزرار، وتتبع النقاط.

4. إضافة ذكاء اصطناعي

يحاول الذكاء الاصطناعي في هذه اللعبة الفوز أولاً من خلال محاولة القيام بحركة فائزة. إذا لم يتمكن من الفوز في الخطوة التالية، فإنه يحاول منع حركة اللاعب الفائزة. إذا لم يكن أي من ذلك ممكنًا، فإنه يقوم بحركة عشوائية.

4.1 فهم منطق الذكاء الاصطناعي

"عقل" الذكاء الاصطناعي موجود في دالة computerMove(). يعطي الأولوية لحركاته بالترتيب التالي:

  1. الفوز: التحقق مما إذا كان بإمكانه وضع "O" في مربع للفوز باللعبة على الفور. يتم التعامل مع هذا بواسطة tryToWin("O").
  2. الصد: إذا لم يتمكن من الفوز، يتحقق مما إذا كان اللاعب على وشك الفوز في دوره التالي ويقوم بصدّه عن طريق وضع "O" في ذلك المكان. يتم التعامل مع هذا بواسطة tryToBlock().
  3. عشوائي: إذا لم يكن أي من الخيارين أعلاه ممكنًا، فإنه يضع "O" في أي مربع عشوائي متاح.

4.2 تعزيز الذكاء الاصطناعي

يمكنك تعزيز الذكاء الاصطناعي بشكل أكبر عن طريق تطبيق خوارزميات مثل Minimax للحصول على خصم لا يمكن هزيمته.

نصيحة: قد يكون تطبيق خوارزمية Minimax معقدًا. ابدأ بمنطق الذكاء الاصطناعي الحالي وأضف المزيد من التعقيد تدريجيًا كلما أصبحت مرتاحًا أكثر.

5. تحسينات وتخصيصات

5.1 إضافة مؤثرات صوتية

عزز تجربة المستخدم عن طريق إضافة مؤثرات صوتية لنقرات الأزرار وأحداث الفوز/التعادل.

5.2 خطوط وأيقونات مخصصة

استخدم خطوطًا مخصصة عن طريق إضافتها إلى مجلد res/font وتحديث أنماطك وفقًا لذلك.

5.3 تحسين الواجهة باستخدام Material Design

استفد من مكونات Material Design للحصول على مظهر عصري واحترافي.

خاتمة

تهانينا! لقد قمت ببناء لعبة إكس-أو احترافية مع خصم ذكاء اصطناعي ذكي، وأنماط مخصصة، ورسوم متحركة. لقد زودك هذا المشروع بمهارات قيمة في تطوير أندرويد، وتطبيق منطق الألعاب، وتصميم واجهة المستخدم وتجربة المستخدم.

شاشة اللعبة تظهر حالة فوز ورسالة 'أنت فاز!'.

لا تتردد في استكشاف المزيد من المشاريع لمواصلة تعزيز مهاراتك.