Назад | Начало урока | Вперед
Содержание

Глава 1

Да! Много работы делается программой при одном щелчке левой кнопкой на поле противника.
Если щелчок произведен на чужом поле, и это естественно "огонь".
То программа должна:
либо поставить крестик - не попал,
либо корабль ранен - звездочку,
либо корабль убит - обрисовать корабль и все пространство вокруг:
либо убиты уже все 10 кораблей - сообщить результат. Конец игры.

Если это наше поле, то просто присваиваем одно из значений переменной m_Decks.
(она может принимать значения 3,2,1 или 0). Напомню, что первоначальное значение
m_Decks равно 4. То есть первый корабль всегда четырехклеточный. Следующих два
- терхклеточный. Далее три двухклеточных, и наконец четыре одноклеточных.


void CField::OnLButtonDown(UINT nFlags, CPoint point) {
POINT pos;
int i, j, IsKilled, sumx, razx, sumy, razy, x, y, cx, cy;
char buf[2];
NET_DATA data;
LISTDATA listdata;

if(m_Operation == PLACEMENT

&& m_Mode == OWN_FIELD
&& m_Parent
&& m_Draw)
{
if(ConvertPropertiesToMassive()) {
m_Count++;
switch(m_Count) {
case 1:
m_Decks = 3;
break;
case 3:
m_Decks = 2;
break;
case 6:
m_Decks = 1;
break;
case 10:
m_Decks = 0;
break;
}
}
}
if(m_Mode == FOREIGN_FIELD
&& m_Operation == FIRE
&& IsNet)
{
NetPoint.x = point.x/20;
NetPoint.y = point.y/20;
if(m_Event[NetPoint.y][NetPoint.x] == SHIPWOUND ||
m_Event[NetPoint.y][NetPoint.x] == SHIPKILLED ||
m_Event[NetPoint.y][NetPoint.x] == CANNOT)
return;
buf[0] = (char)NetPoint.y;
buf[1] = (char)NetPoint.x;

data.buf = buf;
data.lenght = 2;
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SEND_DATA, (LPARAM)&data);
m_Operation = WAIT;

}
if(m_Mode == FOREIGN_FIELD
&& m_Operation == FIRE)
{
point.x = point.x/20;
point.y = point.y/20;
listdata.x = point.x;
listdata.y = point.y;
listdata.str = "ВЫ";
if(m_Event[point.y][point.x] == SHIPKILLED ||
m_Event[point.y][point.x] == SHIPWOUND ||
m_Event[point.y][point.x] == CANNOT ||
m_Event[point.y][point.x] == NEARKILLEDSHIP) return;
m_Steps++;
if(m_Event[point.y][point.x] == SHIP) {
m_GoodSteps++;
IsKilled = SHIPKILLED;
m_Event[point.y][point.x] = SHIPWOUND;

for(i=1; i < 4; i++)

if(m_Event[point.y][point.x+i] != SHIPWOUND || point.x+i==10) break;

if(m_Event[point.y][point.x+i] == SHIP && point.x+i<10)
IsKilled = SHIPWOUND;

for(i=1; i < 4; i++)
if(m_Event[point.y][point.x-i] != SHIPWOUND || point.x-i==-1) break;

if(m_Event[point.y][point.x-i] == SHIP && point.x-i>=0)
IsKilled = SHIPWOUND;

for(i=1; i < 4; i++)
if(m_Event[point.y+i][point.x] != SHIPWOUND || point.y+i==10) break;

if(m_Event[point.y+i][point.x] == SHIP && point.y+i<10)
IsKilled = SHIPWOUND;

for(i=1; i < 4; i++)
if(m_Event[point.y-i][point.x] != SHIPWOUND || point.y-i==-1) break;

if(m_Event[point.y-i][point.x] == SHIP && point.y-i>=0)
IsKilled = SHIPWOUND;

if(IsKilled == SHIPKILLED) {
pos = point;
sumx = razx = sumy = razy = 0;
for(i=1; i<4; i++) {
if(m_Event[pos.y][pos.x+i] == SHIPWOUND && pos.x+i<10) {
m_Event[pos.y][pos.x+i] = SHIPKILLED;
sumx++;
}
if(m_Event[pos.y][pos.x-i] == SHIPWOUND && pos.x-i>=0) {
m_Event[pos.y][pos.x-i] = SHIPKILLED;
razx++;
}
if(m_Event[pos.y+i][pos.x] == SHIPWOUND && pos.y+i<10) {
m_Event[pos.y+i][pos.x] = SHIPKILLED;
sumy++;
}
if(m_Event[pos.y-i][pos.x] == SHIPWOUND && pos.y-i>=0) {
m_Event[pos.y-i][pos.x] = SHIPKILLED;
razy++;
}
}
m_Event[pos.y][pos.x] = SHIPKILLED;
if(pos.x-razx-1>=0) x = pos.x-razx-1;
else x = pos.x-razx;
if(pos.x+sumx+1<10) cx = pos.x+sumx+1;
else cx = pos.x+sumx;
if(pos.y-razy-1>=0) y = pos.y-razy-1;
else y = pos.y-razy;
if(pos.y+sumy+1<10) cy = pos.y+sumy+1;
else cy = pos.y+sumy;
for(i=y; i<=cy; i++)
for(j=x; j<=cx; j++)
if(m_Event[i][j] != SHIPKILLED)
m_Event[i][j] = NEARKILLEDSHIP;
KilledShips++;
InvalidateRect(CRect(x*20, y*20, (cx+1)*20, (cy+1)*20));
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_KILL, 0);
if(KilledShips == 10) {
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_WIN_YOU, 0);
m_Operation = NONE;
}
listdata.state = SHIPKILLED;
}
else {
InvalidateRect(CRect(point.x*20, point.y*20, (point.x+1)*20, (point.y+1)*20));
listdata.state = SHIPWOUND;
}
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SET_MOVE, (LPARAM)&listdata);
}
else {
listdata.state = EMPTY;
m_Event[point.y][point.x] = CANNOT;
m_Operation = NONE;
InvalidateRect(CRect(point.x*20, point.y*20, (point.x+1)*20, (point.y+1)*20));
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SET_MOVE, (LPARAM)&listdata);
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_COMP_MOVE, 0);
}
}
CStatic::OnLButtonDown(nFlags, point);
}

Анализ:

Первая проверка. Если операция - постановка корабля, режим - наше поле, родительское окно,
и можно рисовать
if(m_Operation == PLACEMENT

&& m_Mode == OWN_FIELD
&& m_Parent
&& m_Draw)
{
if(ConvertPropertiesToMassive()) {

Если это наше поле, то просто присваиваем одно из значений переменной m_Decks. (она может принимать значения 3,2,1 или 0). Напомню, что первоначальное значение
m_Decks равно 4. То есть первый корабль всегда четырехклеточный. Следующих два
- терхклеточный. Далее три двухклеточных, и наконец четыре одноклеточных.

m_Count++;
switch(m_Count) {

case 1:
m_Decks = 3;
break;
case 3:
m_Decks = 2;
break;
case 6:
m_Decks = 1;
break;
case 10:
m_Decks = 0;
break;
}
}
Это чтобы понять (определить) корабль какой длины надо рисовать. Ясно, что если в условии
m_Mode == OWN_FIELD (наше поле), то мы будем садить на него корабли при каждом щелчке,
пока не посадим 10 кораблей. При этом значение m_Count будет равно 10.
После чего щелчки на своем поле будут неэффективны.

////////////////////////////////
////////////////////////////////

Теперь - если щелчок произведен на чужом поле, и это естественно "огонь".
То программа должна либо поставить крестик - не попал, либо корабль ранен - звездочку,
либо корабль убит - обрисовать корабль и все пространство вокруг:

if(m_Mode == FOREIGN_FIELD && m_Operation == FIRE) {

Привести переданное в параметре значение point к новому знаменателю (разделить на 20). То есть переведем переданное в параметре значение координат точки в значение клеток.
Например если передано point.x = 20, point.y = 20, то это первая клетка. То есть
курсор щелкнул на первой клетке.

point.x = point.x/20;
point.y = point.y/20;

Присвоить переменной listdata.

listdata.x = point.x;
listdata.y = point.y;
listdata.str = "ВЫ";

Теперь, когда мы знаем на какой клетке щелкнул пользователь,
определим какое значение имеет эта клетка. Таких может быть четыре:
SHIPKILLED, SHIPWOUND, NEARKILLEDSHIP, CANNOT.

Если условие if будет соблюдено, то выход из функции. Нечего щелкать на клетке,
которая уже была щелкнута:
if(m_Event[point.y][point.x] == SHIPKILLED ||
m_Event[point.y][point.x] == SHIPWOUND ||
m_Event[point.y][point.x] == CANNOT ||
m_Event[point.y][point.x] == NEARKILLEDSHIP) return;

Предыдущее условие не было соблюдено, инкременировать m_Steps.
m_Steps++;

При условии, что это корабль
if(m_Event[point.y][point.x] == SHIP) {

m_GoodSteps++;
IsKilled = SHIPKILLED;
m_Event[point.y][point.x] = SHIPWOUND;

Присвоили этой точке значение "корабль ранен". Самому кораблю присвоили значение -
корабль убит. m_GoodSteps увеличили (хороший ход).

Теперь исследуем все точки вокруг той, что передана нам в параметре на предмет того,
действительно ли корабль убит или он только ранен?

В цикле проверяем, что если хоть одна из трех точек справа не ранена
или значение координаты x одной из них равно 10, то выход из цикла.

for(i=1; i<4; i++)
if(m_Event[point.y][point.x+i] != SHIPWOUND || point.x+i==10) break;

К примеру как частный случай. Если все три точки, расположенные справа от переданной
ранены, то оператор break не работает и значение IsKilled == SHIPKILLED остается верно.
Корабль убит. И все дальнейшие проверки if не изменят этого результата.

Если точка справа - корабль и координата x меньше 10,то корабль не убит, он ранен.
Изменить значение IsKilled на SHIPWOUND.

if(m_Event[point.y][point.x+i] == SHIP && point.x+i<10)
IsKilled = SHIPWOUND;

Такая же проверка для точек слева:
for(i=1; i<4; i++)
if(m_Event[point.y][point.x-i] != SHIPWOUND || point.x-i==-1) break;
if(m_Event[point.y][point.x-i] == SHIP && point.x-i>=0)
IsKilled = SHIPWOUND;
Такая же проверка для точек снизу:
for(i=1; i<4; i++)
if(m_Event[point.y+i][point.x] != SHIPWOUND || point.y+i==10) break;
if(m_Event[point.y+i][point.x] == SHIP && point.y+i<10)
IsKilled = SHIPWOUND;

Такая же проверка для точек сверху:
for(i=1; i<4; i++)
if(m_Event[point.y-i][point.x] != SHIPWOUND || point.y-i==-1) break;
if(m_Event[point.y-i][point.x] == SHIP && point.y-i>=0)
IsKilled = SHIPWOUND;

Если все-таки после всех проверок выяснилось, что корабль убит (значение переменной
IsKilled не изменилось), то сначала пометим все точки корабля, которые ранее были
помечены как РАНЕН теперь все эти точки пометим - УБИТ:
if(IsKilled == SHIPKILLED) {

pos = point;
sumx = razx = sumy = razy = 0;
for(i=1; i<4; i++) {
if(m_Event[pos.y][pos.x+i] == SHIPWOUND && pos.x+i<10) {
m_Event[pos.y][pos.x+i] = SHIPKILLED;
sumx++;
}
if(m_Event[pos.y][pos.x-i] == SHIPWOUND && pos.x-i>=0) {
m_Event[pos.y][pos.x-i] = SHIPKILLED;
razx++;
}
if(m_Event[pos.y+i][pos.x] == SHIPWOUND && pos.y+i<10) {
m_Event[pos.y+i][pos.x] = SHIPKILLED;
sumy++;
}
if(m_Event[pos.y-i][pos.x] == SHIPWOUND && pos.y-i>=0) {
m_Event[pos.y-i][pos.x] = SHIPKILLED;
razy++;
}
}

Пометили все точки корабля как убитые.
Переданную в параметре точку тоже пометим как убит:
m_Event[pos.y][pos.x] = SHIPKILLED;

Пометим все точки смежные с убитым кораблем как РЯДОМ_С_УБИТЫМ (NEARKILLEDSHIP).
В это место уже можно не стрелять.

if(pos.x-razx-1>=0) x = pos.x-razx-1;
else x = pos.x-razx;
if(pos.x+sumx+1<10) cx = pos.x+sumx+1;
else cx = pos.x+sumx;
if(pos.y-razy-1>=0) y = pos.y-razy-1;
else y = pos.y-razy;
if(pos.y+sumy+1<10) cy = pos.y+sumy+1;
else cy = pos.y+sumy;
for(i=y; i<=cy; i++)
for(j=x; j<=cx; j++)
if(m_Event[i][j] != SHIPKILLED)
m_Event[i][j] = NEARKILLEDSHIP;

Еще один убитый корабль.
KilledShips++;

Обновить холст, послать сообщение об убитом корабле:
InvalidateRect(CRect(x*20, y*20, (cx+1)*20, (cy+1)*20));
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_KILL, 0);

Если убитых кораблей уже 10, послать сообщение о победе, больше операции не нужны:
if(KilledShips == 10) {

::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_WIN_YOU, 0);
m_Operation = NONE;
}
Корабль убит:
listdata.state = SHIPKILLED;
}


Если IsKilled != SHIPKILLED, если корабль не оказался убитым. Корабль ранен.
Обновить холст. Присвоить listdata.state значение - корабль ранен.
else {

InvalidateRect(CRect(point.x*20, point.y*20, (point.x+1)*20, (point.y+1)*20));
listdata.state = SHIPWOUND;
}
Убит или ранен. В любом случае послать сообщение о том,
что надо делать следующий ход:
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SET_MOVE, (LPARAM)&listdata);

Иначе, если это не корабль, значит мимо, будет ход соперника:

}
else {
listdata.state = EMPTY;
m_Event[point.y][point.x] = CANNOT;
m_Operation = NONE;
InvalidateRect(CRect(point.x*20, point.y*20, (point.x+1)*20, (point.y+1)*20));
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SET_MOVE, (LPARAM)&listdata);
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_COMP_MOVE, 0);
}
}

Да! Много работы делается программой при одном щелчке левой кнопкой на поле противника.
Если щелчок произведен на чужом поле, и это естественно "огонь".
То программа должна:
либо поставить крестик - не попал,
либо корабль ранен - звездочку,
либо корабль убит - обрисовать корабль и все пространство вокруг:
либо убиты уже все 10 кораблей - сообщить результат. Конец игры.

Все эти случаи были рассмотрены выше.
В любом из этих трех случаев программа посылает сообщение.

Если убил:
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_KILL, 0);

Если убил уже 10 кораблей:
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_WIN_YOU, 0);

Если ранил:
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SET_MOVE, (LPARAM)&listdata);

Если не попал:
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SET_MOVE, (LPARAM)&listdata);
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_COMP_MOVE, 0);

А вот как происходит обновление прямоугольника в этих трех случаях:

Если корабль убит, то:
InvalidateRect(CRect(x*20, y*20, (cx+1)*20, (cy+1)*20));
Если корабль ранен или мимо, то:
InvalidateRect(CRect(point.x*20, point.y*20, (point.x+1)*20, (point.y+1)*20));
InvalidateRect(CRect(point.x*20, point.y*20, (point.x+1)*20, (point.y+1)*20));

Интересно, что здесь полю state переменной listdata тоже в зависимости от результата
присваивается одно из трех значений:

listdata.state = SHIPWOUND;
listdata.state = SHIPKILLED;
listdata.state = EMPTY;

Итак:
Если функция определила, что корабль убит, то она помещает во все ячейки двумерного
массива m_Event[i][j] которые принадлежат кораблю значение SHIPKILLED. А во все ячейки
смежные (граничащие) с убитым кораблем помещает значение NEARKILLEDSHIP.

Если функция определила, что корабль ранен, то в ячейку, которой принадлежат переданные
в параметре координаты курсора помещается значение SHIPWOUND.

Если функция определила, что мимо, то в переданную ячейку помещается значение CANNOT.

Кроме того функция помещает соответствующие значения в поле state переменной listdata
тоже в зависимости от результата:
listdata.state = SHIPWOUND;
listdata.state = SHIPKILLED;
listdata.state = EMPTY;

Кроме того функция посылает одно из сообщений:
Если убил:
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_KILL, 0);

Если убил уже 10 кораблей:
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_WIN_YOU, 0);

Если ранил:
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SET_MOVE, (LPARAM)&listdata);

Если не попал:
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SET_MOVE, (LPARAM)&listdata);
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_COMP_MOVE, 0);

::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_KILL, 0);
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_WIN_YOU, 0);
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SET_MOVE, (LPARAM)&listdata);
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_SET_MOVE, (LPARAM)&listdata);
::SendMessage(m_Parent->m_hWnd, WM_STATIC_TO_DLG, WM_COMP_MOVE, 0);

Кроме того в функции происходит обновление прямоугольника в этих трех случаях:
Если корабль убит, то:
InvalidateRect(CRect(x*20, y*20, (cx+1)*20, (cy+1)*20));

Если корабль ранен или мимо, то:
InvalidateRect(CRect(point.x*20, point.y*20, (point.x+1)*20, (point.y+1)*20));
InvalidateRect(CRect(point.x*20, point.y*20, (point.x+1)*20, (point.y+1)*20));


Назад | Начало урока | Вверх | Вперед
Содержание

Hosted by uCoz