Консультация № 182842
13.04.2011, 15:51
53.35 руб.
0 12 1
Здравствуйте! У меня возникли сложности с таким вопросом:
Реализовать игру УГОЛКИ в визуальной среде C++ Builder 6. Играет человек с человеком.
Правила уголков
Цель игры - переставить все свои шашки в дом соперника. Игрок сделавший это первым выигрывает.
Шахматная доска размерностью 8х8 клеток.
В начальном положении шашки обоих игроков занимают свои стартовые позиции(дома). Дом может имеет два варианта размещения шашек: 3х3 и углом (1-2-3-4).
Каждый игрок может за один ход переместить одну шашку. Шашки можно перемещать в любом направлении на соседнюю пустую клетку, шашки могут перепрыгивать через свои и чужие шашки. Перепрыгивать можно по вертикали, либо по горизонтали, если за шашкой есть пустая клетка.
Прыжки могут быть многократными, при этом перепрыгивать шашка может, как свои шашки, так и шашки противника. Длина прыжка не принудительна, т.е. игрок может решить в любое время прекратить многократный ход.

Примечание
В главном меню находится
Игра:
Новая игра ,Загрузить игру, Сохранить игру, Выход.
Настройки (В которых мы выбираем размещение шашек)
Справка: Правила игры.

Обсуждение

Неизвестный
20.04.2011, 22:58
общий
это ответ
Здравствуйте, Иванов Иван!
Насколько я поняла, диагональные ходы запрещены, серия ходов разрешена только при прыжках. Если что, легко изменить оба эти условия.
Класс ячейки с шашкой взят из вопроса 182840, остальной интерфейс тоже не особо изменился.
Сохранение делается через бинарный файл, правка сохранения вручную невозможна, зато легко писать и читать матрицу поля. Сохраняется только текущее состояние, время, режим и чей ход. Если во время сохранения ход был начат, он не сохранится.
В приложении основной код, в архиве проект.
Удачи!

Приложение:
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "ChainMain.h"
#include "Option.h"
#include "rules.h"
#include <math.h>
#include <stdio.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{ //инициализация переменных, обнуление поля
wfield = 8;
move = false;
movebegin = false;
movei = -1; movej = -1;
gamer = 0;
bl[0] = 0; bl[1] = 0;
itime = 0;
col[0] = clBlack;
col[1] = clRed;
mode = 9;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
int w = Panel1->Width / wfield; //создание визуального поля
for (int i=0, top = 0; i<wfield; i++, top+=w) {
for (int j=0, left = 0; j<wfield; j++, left+=w) {
blocks[i][j] = new BlockControl (this);
blocks[i][j]->Setup (w, i*wfield+j+1, i, j, clWhite);
blocks[i][j]->Parent = Panel1;
blocks[i][j]->Left = left;
blocks[i][j]->Top = top;
blocks[i][j]->OnClick = BlockClick;
blocks[i][j]->Show();
}
}
Set3x3(); //расстановка шашек

}
//---------------------------------------------------------------------------
void __fastcall TForm1::BlockClick (TObject *Sender)
{
BlockControl* bc = (BlockControl*)Sender;
bool wasjump = jump; //проверка, идёт ли серия прыжков
if (!move && bc->gamer!=gamer) { //выход, если ход не идёт и это не шашка текущего игрока
bc->Down = false;
return;
}
if (move && !movebegin && movei==bc->i && movej==bc->j) {
bc->Down = false; //отмена движения текущей шашки, если игрок нажал повторно
move = false;
return;
} else if (move && movebegin && movei==bc->i && movej==bc->j) {
bc->Down = true; //выход, если ход начался и нажата шашка
return;
}
if (move && !CheckMovePossibility(bc->i, bc->j)) {
bc->Down = false; //выход, если ход идёт и на данную клетку нельзя
return;
}
if (move && movebegin && jump!=wasjump) {
bc->Down = false; //выход, если идёт серия прыжков, а ход - не прыжок
jump = wasjump; //сохранение предыдущего состояния
return;
}
if (move) { //если ход уже идёт
movebegin = true; //ход начался
bc->SetUsed(true, gamer, col[gamer]); //устанавливаем занятость
blocks[movei][movej]->SetUsed(false); //стираем предыдущую
flds[bc->i][bc->j] = gamer+1; //ставим флаг на поле
flds[movei][movej] = 0;
movei = bc->i;
movej = bc->j;
if (!jump) Button2Click (NULL); //если не было прыжка - заканчиваем ход
} else {
move = true; //игрок просто выбрал шашку, ход не сделан
movei = bc->i;
movej = bc->j;
return;
}

if (CheckWin()!=-1) { //проверка, привёл ли ход к выигрыщу
ShowMessage ("Игрок#"+IntToStr(gamer+1)+" победил!");
Timer1->Enabled = false;
Panel1->Enabled = false;
}

}
//расстановка 3х3
void TForm1::Set3x3 ()
{
for (int i=0; i<wfield; i++)
for (int j=0; j<wfield; j++) {
blocks[i][j]->SetUsed(false, 0);
flds[i][j] = 0;
}
for (int i=wfield-3; i<wfield; i++)
for (int j=0; j<3; j++) {
blocks[i][j]->SetUsed(true, 0, col[0]);
flds[i][j] = 1;
blocks[j][i]->SetUsed(true, 1, col[1]);
flds[j][i] = 2;
}
}

void TForm1::SetUgol() //расстановка углом
{
for (int i=0; i<wfield; i++)
for (int j=0; j<wfield; j++){
blocks[i][j]->SetUsed(false, 0);
flds[i][j] = 0;
}
for (int i=wfield-4, k = 1; i<wfield; i++, k++)
for (int j=0; j<k; j++) {
blocks[i][j]->SetUsed(true, 0, col[0]);
flds[i][j] = 1;
blocks[j][i]->SetUsed(true, 1, col[1]);
flds[j][i] = 2;
}
}

bool TForm1::CheckMovePossibility (int x, int y) //проверка возможности хода
{
int dx = abs (movei - x); //проверяем расстояние
int dy = abs (movej - y);
if (dx>2 || dy>2) return false; //0, если расстояние большое
if (dx>0 && dy>0) return false; //Запрет диагональных ходов
if (blocks[x][y]->IsUsed()) return false; //0, если клетка занята
if (dx<2 && dy<2) {jump = false; return true;} //1, если соседняя
int sx = dx!=0 ? dx/(movei - x) : 0; //проверяем направление
int sy = dy!=0 ? dy/(movej - y) : 0;
if (movei == x) { //проверяем прыжки
if (flds[x][y+sy]==0) return false;
else {jump = true; return true;}
} else if (movej==y) {
if (flds[x+sx][y]==0) return false;
else {jump = true; return true;}
} else {
return false; //запрет диагональных прыжков (ниже код разрешения)
//if (flds[x+sx][y+sy]==0) return false;
//else return true;
}
}

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{ //считаем время
itime++;
time->Caption = IntToStr (itime);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{ //начать заново
for (int i=0; i<wfield; i++)
for (int j=0; j<wfield; j++) {
blocks[i][j]->SetUsed(false, 0);
flds[i][j] = 0;
}
if (mode==9) Set3x3(); //расстановка в зависимости от настроек
else SetUgol();
move = false; movebegin = false;
gamer = 0;
bl[0] = 0; bl[1] = 0;
itime = 0;
col[0] = clBlack;
col[1] = clRed;
Timer1->Enabled = true;
Panel1->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
move = false; //завершение хода
movebegin = false;
blocks[movei][movej]->Down = false;
gamer = !gamer; //смены игроков
nGamer->Caption = IntToStr (gamer+1);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::N5Click(TObject *Sender)
{
Form2->SetMode(mode); //настройка
if (Form2->ShowModal()==mrOk) {
mode = Form2->mode;
Button1Click (Sender);
}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::N4Click(TObject *Sender)
{
Application->Terminate(); //выход
}
//---------------------------------------------------------------------------

void __fastcall TForm1::N7Click(TObject *Sender)
{
Form3->ShowModal(); //справка
}
//---------------------------------------------------------------------------
int TForm1::CheckWin() //проверка выигрыша
{
int gs[2];
gs[0] = 0; gs[1] = 0; //считаем фишки врага в доме
if (mode==9) {
for (int i=wfield-3; i<wfield; i++)
for (int j=0; j<3; j++) {
if (flds[i][j] == 2) gs[0]++;
if (flds[j][i] == 1) gs[1]++;
}
if (gs[0]==9) return 0; //если все фишки в доме - победа
else if (gs[1]==9) return 1;
else return -1;

} else {
for (int i=wfield-4, k = 1; i<wfield; i++, k++)
for (int j=0; j<k; j++) {
if (flds[i][j] == 2) gs[0]++;
if (flds[j][i] == 1) gs[1]++;
}
if (gs[0]==10) return 0;
else if (gs[1]==10) return 1;
else return -1;
}
}
void __fastcall TForm1::N2Click(TObject *Sender)
{
if (SaveDialog1->Execute()) { //сохранение в файл
FILE* f = fopen (SaveDialog1->FileName.c_str(), "wb");
fwrite (flds, sizeof(int), MaxBlocks*MaxBlocks, f);
fwrite (&mode, sizeof(int), 1, f);
fwrite (&gamer, sizeof(bool), 1, f);
fwrite (&itime, sizeof(int), 1, f);
fclose (f);
}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::N3Click(TObject *Sender)
{
if (OpenDialog1->Execute()) {
Button1Click (Sender); //загрузка из файла
FILE* f = fopen (SaveDialog1->FileName.c_str(), "rb");
fread (flds, sizeof(int), MaxBlocks*MaxBlocks, f);
fread (&mode, sizeof(int), 1, f);
fread (&gamer, sizeof(bool), 1, f);
nGamer->Caption = IntToStr (gamer+1);
fread (&itime, sizeof(bool), 1, f);
Timer1->Enabled = true;
move = false;
time->Caption = IntToStr (itime);
fclose (f);
for (int i=0; i<wfield; i++)
for (int j=0; j<wfield; j++)
if (flds[i][j]==0)
blocks[i][j]->SetUsed(false, 0);
else if (flds[i][j]==1)
blocks[i][j]->SetUsed(true, 0, col[0]);
else if (flds[i][j]==2)
blocks[i][j]->SetUsed(true, 1, col[1]);

}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::N1Click(TObject *Sender)
{
Button1Click (Sender); //новая игра
}
//---------------------------------------------------------------------------
Прикрепленные файлы:
Неизвестный
21.04.2011, 22:02
общий
Уважаемая Алена, не могли бы вы сделать чтобы завершения хода происходило не тока по нажатию кнопки "Закончить ход" ,но и по второму нажатию по фишке которую мы передвинули
Неизвестный
21.04.2011, 23:35
общий
МогуСделаю завтра, это не сложно. А правила я правильно поняла, ничего больше не надо менять?
Неизвестный
22.04.2011, 00:05
общий
Нет не надо)

BlockControl* bc = (BlockControl*)Sender;// в 51 строчке
поесните пожалуйста)
Неизвестный
22.04.2011, 00:13
общий
Когда кнопка (а ячейка у нас - это кнопка) нажимается, вызывается обработчик события OnClick, он для всех кнопок одинаковый. Зато параметр Sender обозначает объект, вызвавший событие, если мы приводим его к типу нашей кнопки, то узнаём, какая именно ячейка была нажата.
Неизвестный
22.04.2011, 19:22
общий
А зачем делалось CBlockControl.cpp Chain.cpp Option.cpp в виде отдельных файлов?
Возможно ли поместить все в один файл ChainMain.cpp?Если да то как?
Неизвестный
23.04.2011, 15:52
общий
Для завершения хода замените эту функцию
[code h=100]void __fastcall TForm1::BlockClick (TObject *Sender)
{
BlockControl* bc = (BlockControl*)Sender;
bool wasjump = jump; //ïðîâåðêà, èä¸ò ëè ñåðèÿ ïðûæêîâ
if (!move && bc->gamer!=gamer) { //âûõîä, åñëè õîä íå èä¸ò è ýòî íå øàøêà òåêóùåãî èãðîêà
bc->Down = false;
return;
}
if (move && !movebegin && movei==bc->i && movej==bc->j) {
bc->Down = false; //îòìåíà äâèæåíèÿ òåêóùåé øàøêè, åñëè èãðîê íàæàë ïîâòîðíî
move = false;
return;
} else if (move && movebegin && movei==bc->i && movej==bc->j) {
//bc->Down = true; //âûõîä, åñëè õîä íà÷àëñÿ è íàæàòà øàøêà
Button2Click (NULL);
return;
}
if (move && !CheckMovePossibility(bc->i, bc->j)) {
bc->Down = false; //âûõîä, åñëè õîä èä¸ò è íà äàííóþ êëåòêó íåëüçÿ
return;
}
if (move && movebegin && jump!=wasjump) {
bc->Down = false; //âûõîä, åñëè èä¸ò ñåðèÿ ïðûæêîâ, à õîä - íå ïðûæîê
jump = wasjump; //ñîõðàíåíèå ïðåäûäóùåãî ñîñòîÿíèÿ
return;
}
if (move) { //åñëè õîä óæå èä¸ò
movebegin = true; //õîä íà÷àëñÿ
bc->SetUsed(true, gamer, col[gamer]); //óñòàíàâëèâàåì çàíÿòîñòü
blocks[movei][movej]->SetUsed(false); //ñòèðàåì ïðåäûäóùóþ
flds[bc->i][bc->j] = gamer+1; //ñòàâèì ôëàã íà ïîëå
flds[movei][movej] = 0;
movei = bc->i;
movej = bc->j;
if (!jump) Button2Click (NULL); //åñëè íå áûëî ïðûæêà - çàêàí÷èâàåì õîä
} else {
move = true; //èãðîê ïðîñòî âûáðàë øàøêó, õîä íå ñäåëàí
movei = bc->i;
movej = bc->j;
return;
}

if (CheckWin()!=-1) { //ïðîâåðêà, ïðèâ¸ë ëè õîä ê âûèãðûùó
ShowMessage ("Èãðîê#"+IntToStr(gamer+1)+" ïîáåäèë!");
Timer1->Enabled = false;
Panel1->Enabled = false;
}

}
[/code]
По поводу одного файла. В принципе, конечно, можно, только зачем?Вообще считается хорошим тоном оформлять каждый класс отдельным файлом, это удобно и упрощает разработку, потому что рыться в огромном файле, где смешано всё на свете, не слишком приятно. К тому же, борланд автоматически оформляет каждую форму отдельным файлом, постаравшись, я думаю, можно заставить его нормально работать с одним, но это нетривиальная и бессмысленная задача.
Неизвестный
23.04.2011, 19:38
общий
Спасибо Алена все работает в лучшем виде!
А нет у вас этой функции в каком то другом виде?
А то коментарии отображаются в виде не понятных символоах.
:)
Неизвестный
23.04.2011, 19:44
общий
ПожалуйстаНе заметила, что кодировка испортилась. Вот нормальная:
[code h=100]void __fastcall TForm1::BlockClick (TObject *Sender)
{
BlockControl* bc = (BlockControl*)Sender;
bool wasjump = jump; //проверка, идёт ли серия прыжков
if (!move && bc->gamer!=gamer) { //выход, если ход не идёт и это не шашка текущего игрока
bc->Down = false;
return;
}
if (move && !movebegin && movei==bc->i && movej==bc->j) {
bc->Down = false; //отмена движения текущей шашки, если игрок нажал повторно
move = false;
return;
} else if (move && movebegin && movei==bc->i && movej==bc->j) {
//bc->Down = true; //выход, если ход начался и нажата шашка
Button2Click (NULL);
return;
}
if (move && !CheckMovePossibility(bc->i, bc->j)) {
bc->Down = false; //выход, если ход идёт и на данную клетку нельзя
return;
}
if (move && movebegin && jump!=wasjump) {
bc->Down = false; //выход, если идёт серия прыжков, а ход - не прыжок
jump = wasjump; //сохранение предыдущего состояния
return;
}
if (move) { //если ход уже идёт
movebegin = true; //ход начался
bc->SetUsed(true, gamer, col[gamer]); //устанавливаем занятость
blocks[movei][movej]->SetUsed(false); //стираем предыдущую
flds[bc->i][bc->j] = gamer+1; //ставим флаг на поле
flds[movei][movej] = 0;
movei = bc->i;
movej = bc->j;
if (!jump) Button2Click (NULL); //если не было прыжка - заканчиваем ход
} else {
move = true; //игрок просто выбрал шашку, ход не сделан
movei = bc->i;
movej = bc->j;
return;
}

if (CheckWin()!=-1) { //проверка, привёл ли ход к выигрыщу
ShowMessage ("Игрок#"+IntToStr(gamer+1)+" победил!");
Timer1->Enabled = false;
Panel1->Enabled = false;
}

}
[/code]
Неизвестный
23.04.2011, 21:23
общий
Спасибо) Вы лучшая!
Неизвестный
23.04.2011, 23:07
общий
:)
Неизвестный
29.04.2011, 20:26
общий
Добрый вечер! Подскажит пожалуйста за что отвечают две логические переменные: movebegin, move?
Форма ответа