Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Парные картинки на C#


Автор: kelmiir 3.5.2013, 14:17
Приветствую обитателей форума и прошу помощи.
C# начал изучать совсем недавно и до этого сталкивался только с Паскалем
По заданию нужно разработать игру "Парные картинки"
Приложение сделать получилось но работает как попало (пары не всегда убираются, парой открываются по 3 картинки и т.д.).
Полагаю ошибка где то в логике обработки кликов по картинкам но как исправить не знаю.

Код

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


namespace Курсовая
{
    public partial class mein : Form
    {
        const int nw = 6; // кол-во клеток по горизонтали
        const int nh = 6; // кол-во клеток по вертикали
        const int np = (nw * nh) / 2; // кол-во пар картинок
        System.Drawing.Graphics g; // рабочие графическое поле
        Bitmap pics; // картинки (загружаются из файла)
        int cw, ch; // размеры клетки(картинки)
        int[,] field = new int[nw, nh]; // игровое поле

// field[i,j] == 1...k - клетка закрытая (k - номер клетки)
// field[i,j] == 101 ... (100+k)  - клетка открыта ( игрок видит картинку)
// field[i,j] == 201 ... (200+k) - клетке картинка для которой найдена пара

        int nopened = 0; //кол-во открытых клеток (найденных) пар картинок
        int copened = 0; // кол-во открытых клеток в данный момент
        int[] open1 = new int[2]; // координаты 1й открытой клетки
        int[] open2 = new int[2]; // координаты 2й открытой клетки
        System.Windows.Forms.Timer timer1; // таймер

// нарисовать картинку
// картинку если клетка открыта
// границу если клетка закрыта
// вон если в клетке картинка для которой найдена пара

        private void cell(int i, int j)
        {
            int x, y; // координаты верхнего левого угла

// между картинками 
// остается промежуток в 1 пиксель

            x = i * (cw + 2);
            y = j * (ch + 2) + meinmenu.Height;

            if (field[i, j] > 200)

// для этой клетки найдена пара
// картинку отображать не надо

                g.FillRectangle(SystemBrushes.Control, x, y, cw + 2, ch + 2);

            if ((field[i, j] > 100) && (field[i, j] < 200))
            {
// клетка открыта - отображать картинку 

                g.DrawImage(pics,
                    new Rectangle(x + 1, y + 1, cw, ch),
                    new Rectangle((field[i, j] - 101) * cw, 0, cw, ch),
                    GraphicsUnit.Pixel);
                g.DrawRectangle(Pens.Black, x + 1, y + 1, cw, ch);
            }
            if ((field[i, j] > 0) && (field[i, j] < 100))
            {
// клетка закрыта, рисуем контур
                g.FillRectangle(SystemBrushes.Control, x + 1, y + 1, cw, ch);
                g.DrawRectangle(Pens.Black, x + 1, y + 1, cw, ch);
            }
        }

// нарисовать поле

        private void drawfield()
        {
            for (int i = 0; i < nw; i++)
                for (int j = 0; j < nh; j++)
                    this.cell(i, j);
        }

// новая игра
        private void newgame()
        {
// распределяем пары картинок по игровому полю
// запишем в массив field случайные числа
// от 1 до k, где k - количество картинок
// каждое число число должно быть записано
// в массив 2 раза

            Random rnd; // генератор случайных чисел
            int rndn; // случайное число
            rnd = new Random();
            int[] buf = new int[np];

// np - кол-во картинок
// в buf(i) Записываем сколько i чисел
//(идентификаторов картинок) записано в массив field
            for (int i = 0; i < nw; i++)
                for (int j = 0; j < nh; j++)
                {
                    do
                    {
                        rndn = rnd.Next(np) + 1;
                    } while (buf[rndn - 1] == 2);
                    field[i, j] = rndn;
                    buf[rndn - 1]++;
                }
            nopened = 0;
            copened = 0;
            this.drawfield();
        }




        public mein()
        {

            InitializeComponent();
            this.MouseClick += new MouseEventHandler(mein_MouseClick);
            try
            {
// загружаем файл с картинками
                pics = new Bitmap("pictures.bmp");
            }
            catch (Exception exc)
            {
                MessageBox.Show("Файл 'pictures.bmp' не найден.\n" +
                    exc.ToString(), "Парные картинки",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                this.Close();
                return;
            }

// определяем размер картинки и устанавливаем
// размер клеток игрового поля
            cw = (int)(pics.Width / np);
            ch = pics.Height;

// установить размер клиентской области формы
// в соответствие с размером картинок и их кол-ва
// (смотреть определение констант cw и ch

            this.ClientSize =
                new System.Drawing.Size(nw * (cw + 2) + 1,
                    nh * (ch + 2) + 1 + meinmenu.Height);

// работа с графической поверхностью

            g = this.CreateGraphics();

// создать объект Timer1
            timer1 = new Timer();
            timer1.Tick +=
                new System.EventHandler(this.timer1_Tick);
            timer1.Interval = 200;
            newgame();
            
        }
        
// щелчок по игровому полю

        private void mein_MouseClick(object sender, MouseEventArgs e)
        {
            {
// индексы элементов массива field 
// соответствуещего клетке в которой
// сделан щелчок
                int i, j;

                i = e.X / (cw + 3);
                j = (e.Y - meinmenu.Height) / (ch + 3);

// если таймер работает, это значит, что в данный
// момент открыты две клетки, в которых находятся
// одинаковые картинки, но они ещё не "стерты"
// если щелчок сделан в одной из этих картинок,
// то ничего делать не надо.

                if ((timer1.Enabled) && (field[i, j] > 200))
                {
                    return;
                }

// щелчок на месте одной из двух уже найденных
// пар картинок

                if (field[i, j] > 200) return;

// открытых картинок нет
                if (copened == 0)
                {
                    copened++;

// записываем координаты 1й открытой клетки
                    open1[0] = 1; open1[1] = j;
                    field[i, j] += 100; // клетка помечена как открытая
                    this.cell(i, j); // отрисовать клетку
                    return;
                }
// открыта одна клетка, надо открыть вторую
                if (copened == 1)
                {
// записываем координаты 2й открытой клетки
                    open2[0] = i; open2[1] = j;

// если открыта одна клетка , и щелчок сделан 
// в той же клетке, ничего не происходит
                    if ((open1[0] == open2[0]) && (open1[1] == open2[1]))
                        return;
                    else
                    {
// теперь открыты 2 клетки
                        copened++;

                        field[i, j] += 100; // клетка помечена как открытая
                        this.cell(i, j); // отрисовываем клетку

// открыты 2 одинаковые картинки
                        if (field[open1[0], open1[1]] == field[open2[0], open2[1]])
                        {
                            nopened++;
// пометим клетки как найденые
                            field[open1[0], open1[1]] += 100;
                            field[open2[0], open2[1]] += 100;
                            copened = 0;
// запускае таймер. процедура обработки
// сигнала от таймер "сотрет" клетки
// которые с одинаковыми картинками
                            timer1.Enabled = true;
                        }
                    }
                    return;
                }
// открыты 2 клетки но в них разные картинки,
// закроем их и откроем ту клетку, в которой сделан щелчок

                if (copened == 2)
                {
// закрываем открытые клетки
                    field[open1[0], open1[1]] -= 100;
                    field[open2[0], open2[1]] -= 100;
                    this.cell(open1[0], open1[1]);
                    this.cell(open2[0], open2[1]);
// записываем в open1 номер текущей клетки

                    open1[0] = i; open1[1] = j; 
                    copened = 1; // счетчик открытых клето
// открыть клетку в которой сделан щелчок
                    field[i, j] += 100;
                    this.cell(i, j);
                }
            } 
        }
// команда новая игра

        private void newgameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            newgame();
        }
// обработка события таймер
        private void timer1_Tick(object sender, System.EventArgs e)
        {
// отрисовать клетку
            this.cell(open1[0], open1[1]);
            this.cell(open2[0], open2[1]);
// остановить таймер
            timer1.Enabled = false;
            if (nopened == np)
            {
                MessageBox.Show("Вы спавились с поставленной задачей!");
            }
        }
        private void mein_Paint(object sender, PaintEventArgs e)
        {
            drawfield(); // отрисовать игровое поле
        }

        private void infoprogramToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox1 aboutbox;
            aboutbox = new AboutBox1();
            aboutbox.Show();
           
        }
    }
}

Автор: Экскалупатор 5.5.2013, 12:56
я бы начал с приведения всего кода в адекватный ЧИТАЕМЫЙ вид. надо дать понятные названия переменным. иначе что бы разобраться в этой писанине потребуется немало времени.
Цитата(kelmiir @  3.5.2013,  13:17 Найти цитируемый пост)
open2[0] = i; open2[1] = j;

тут как мне показалось какая то странная попытка хранить координаты в массиве, зачем? странность в том, что у тебя, как я понял, хранятся разные координаты в одном массиве и ты просто должен помнить что [0] - это по x, а [1] - это у. вероятность запутаться увеличивается в разы. да и не понятно что такое "open2" без прочтения кода. а должно быть понятно, смотришь на строку и сразу понятно что и откуда берется. я думаю если ты в нормальный вид приведешь свой код, то и выяснится по пути то почему не работает.

Автор: kelmiir 5.5.2013, 15:59
Извините об этом как то не задумался..Спасибо за совет теперь добавил в код комментарии.
Делал по пособию по этому сам не до конца понимаю, что да как в этом коде.
Код пришлось слегка переделать под мое задание да и немного ошибок в печати было.
Нашел малость недочетов ещё у себя но по прежнему работает приложение не правильно.
Почему то всегда отображается картинка открытая по первому клику даже если ей находится пара.

Автор: Экскалупатор 5.5.2013, 22:44
Цитата(kelmiir @  3.5.2013,  13:17 Найти цитируемый пост)
 if (field[i, j] > 200)
// для этой клетки найдена пара
// картинку отображать не надо
                g.FillRectangle(SystemBrushes.Control, x, y, cw + 2, ch + 2);
            if ((field[i, j] > 100) && (field[i, j] < 200))
            {
// клетка открыта - отображать картинку 
                g.DrawImage(pics,
                    new Rectangle(x + 1, y + 1, cw, ch),
                    new Rectangle((field[i, j] - 101) * cw, 0, cw, ch),
                    GraphicsUnit.Pixel);
                g.DrawRectangle(Pens.Black, x + 1, y + 1, cw, ch);
            }
            if ((field[i, j] > 0) && (field[i, j] < 100))
            {
// клетка закрыта, рисуем контур
                g.FillRectangle(SystemBrushes.Control, x + 1, y + 1, cw, ch);
                g.DrawRectangle(Pens.Black, x + 1, y + 1, cw, ch);
            }


а может быть (field[i, j] == 100 или (field[i, j] == 200 или (field[i, j] == 0
???
или такие ситуации невозможны?

дальше, в этом куске:
Цитата(kelmiir @  3.5.2013,  13:17 Найти цитируемый пост)
// открыта одна клетка, надо открыть вторую
                if (copened == 1)
                {
// записываем координаты 2й открытой клетки
                    open2[0] = i; open2[1] = j;
// если открыта одна клетка , и щелчок сделан 
// в той же клетке, ничего не происходит
                    if ((open1[0] == open2[0]) && (open1[1] == open2[1]))
                        return;
                    else
                    {
// теперь открыты 2 клетки
                        copened++;
                        field[i, j] += 100; // клетка помечена как открытая
                        this.cell(i, j); // отрисовываем клетку
// открыты 2 одинаковые картинки
                        if (field[open1[0], open1[1]] == field[open2[0], open2[1]])
                        {
                            nopened++;
// пометим клетки как найденые
                            field[open1[0], open1[1]] += 100;
                            field[open2[0], open2[1]] += 100;
                            copened = 0;
// запускае таймер. процедура обработки
// сигнала от таймер "сотрет" клетки
// которые с одинаковыми картинками
                            timer1.Enabled = true;
                        }
                    }
                    return;
                }
// открыты 2 клетки но в них разные картинки,
// закроем их и откроем ту клетку, в которой сделан щелчок
                if (copened == 2)
                {
// закрываем открытые клетки
                    field[open1[0], open1[1]] -= 100;
                    field[open2[0], open2[1]] -= 100;
                    this.cell(open1[0], open1[1]);
                    this.cell(open2[0], open2[1]);
// записываем в open1 номер текущей клетки
                    open1[0] = i; open1[1] = j; 
                    copened = 1; // счетчик открытых клето
// открыть клетку в которой сделан щелчок
                    field[i, j] += 100;
                    this.cell(i, j);
                }
            } 

я так понимаю, что при попадании в первый иф и если картинки две но не одинаковые то из метода обработки мы все равно выйдем, но картинки не уберем с экрана? т.е. return в конце первого ифа сработает в любом случае и во второй иф мы не попадем никогда, только при следующем заходе в метод. или тут как то по другому работает?

p.s. я про понятность кода говорил не про коментарии, а про то что надо давать нормальные названия переменным.
как из записи:
if (field[open1[0], open1[1]] == field[open2[0], open2[1]])
можно понять что вообще тут с чем сравнивается? приходится постоянно бегать в начало листинга и смотреть че это вообще за хрень и за что она отвечает?
если бы было написано как то так(просто пример):
if (firstSelectedImage == secondSelectedImage)
то все было бы на порядок понятнее и читаемее и уже давно можно было бы найти все ошибки.

p.p.s. если собираешься заниматься этим в дальнейшем(после сдачи "курсовой"), пусть даже на другом языке, то очень рекомендую научиться делать нормальные имена и оформление кода в целом.

Автор: kelmiir 7.5.2013, 10:08
Если честно как и писал выше делал по пособию..и как раз откуда идут эти значения 0, 100, 200, и тд да и вообще почему именно они как раз не могу понять.

Автор: Экскалупатор 7.5.2013, 12:05
посмотри на метод который обрабатывает клик по картинкам, я описал выше что меня смущает, может от этого и ошибки появляются

Автор: kelmiir 8.5.2013, 09:28
По первому я этот кусок со значениями сам не понимаю откуда они берутся.
По второму убирание ретурна после первого ифа на общее положение не влияет по прежнему не правильно работает.

Автор: Экскалупатор 8.5.2013, 21:13
Цитата(kelmiir @  8.5.2013,  08:28 Найти цитируемый пост)
По второму убирание ретурна после первого ифа на общее положение не влияет по прежнему не правильно работает. 

))) я же не предлагал его убрать. я говорю что в этом месте как то странно работает. я, так сказать, пытаюсь указать на проблемное, с моей точки зрения, место. дебажить у меня не особо есть временя, но мне кажется проблема в этих ифах. точнее в их взаимодействии.

Автор: kelmiir 9.5.2013, 11:42
В работе алгоритма ошибок не оказалось) Это была моя невнимательность и немного плохого плохо напечатаная и инструкция) в определение координат перепутал 1 с буковой i ) по этому и работал алгоритм не правильно) Но есть ещё одна сложность с которой я не знаю как справится. Заключается в том что когда сворачиваю игру то все клетки перестают отображаться и приходиться начинать игру заново. Без исправления этой ошибки отказываются принимать работу =(

Автор: Экскалупатор 9.5.2013, 13:02
надо вызвать метод перерисовки формы(и всего содержимого в ней). обрабатывай события(shown, invalidate или тип того)
посмотри тут все они описаны http://msdn.microsoft.com/ru-ru/library/system.windows.forms.form.aspx

Автор: kelmiir 9.5.2013, 13:52
Либо я не понимаю в целом метод либо совсем что то делаю не правильно. Попробовал разные методы но так ничего и не вышло. Куда его хотя бы примерно нужно вставить?
Попробовал и на саму прорисовку ставить , на форму и на загрузку формы. Ничего из этого не вышло

Автор: Machaon 15.5.2013, 23:29
Делай так:

Код

private void Form1_Deactivate(object sender, EventArgs e)
        {
            if (this.WindowState == System.Windows.Forms.FormWindowState.Minimized)
            {
             // Записываешь состояние поля
            }
        }
 
        private void Form1_Activated(object sender, EventArgs e)
        {
         //  Восстанавливаешь состояние поля
        }

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)