Федеральное агентство железнодорожного транспорта Омский государственный университет путей сообщения Кафедра «Автоматика и системы управления» ПРОГРАММА «МОРСКОЙ БОЙ» Пояснительная записка к курсовой работе по дисциплине «Программирование» ИНМВ.400000.000 ПЗ | |
Студента гр. 26м _____________ «__»_________ 2017 г. Руководитель – преподаватель кафедры АиСУ _____________ «__»_________ 2017 г. | |
Омск 2017 |
Реферат
УДК 004.42
Пояснительная записка к курсовой работе содержит 23 страницы, 7 рисунков, 3 использованных источника.
Объектом курсовой работы является консольная игра «Морской бой».
Цель курсовой работы – получение основных навыков использования языка Си, создание игры с искусственным интеллектом.
Результатом курсовой работы является игра «Морской бой», написанная на языке Си в программе Code Blocks.
В процессе создания игры была изучена лексика языка Си.
Пояснительная записка выполнена в текстовом редакторе Microsoft Word 2007.
Введение
Язык Си – это универсальный язык программирования, который применяется в различных сферах программирования. Изначально язык Си создавался для написания системного программного обеспечения, но он может использоваться и для написания прикладных программ и игр.
Правильно написанный код на этом языке будет работать быстро, потому что этот язык имеет как высокоуровневые, так и низкоуровневые средства для взаимодействия с оборудованием. Это позволяет писать программы, которые учитывают особенности архитектуры и могут использовать эти особенности для достижения наибольшего быстродействия. Программы на языке Си отличаются лаконичностью и понятностью. Это достигается за счет того, что в языке Си применяется структурное программирование, позволяющее программисту легко описывать структуру программы, в которой каждый кусок программного кода будет выполнять свою задачу и каждый из них будет логически отделен от другого.
Курсовая работа является примером использования языка Си и графической библиотеки SDL для написания графической игры.
Содержание
Введение. 3
1 Правила игры.. 5
2 Реализация программного кода с комментариями. 6
3 Инструкция пользователя. 13
Заключение. 16
Библиографический список. 17
од программы.. 18
1 Правила игры
Морской бой – это логическая игра для двух игроков, целью каждого из которых является нахождение всех кораблей противника.
На поле 8 на 8 игрок каждый располагает определенное количество кораблей. Игроки должны подстрелить все корабли противника. При каждом выстреле игрок выбирает клетку поля. При этом другой игрок сообщает, попал ли выстрел в корабль. Выигрывает тот, кто быстрее уничтожит корабли противника.
2 Реализация программного кода с комментариями
В программе используются библиотеки:
#include <SDL2/SDL. h>
#include <stdio. h>
#include <stdlib. h>
#include <time. h>
Для упрощения разработки игры все функции были разделены на классификации. Их объявления и реализации размещены в разных файлах. С помощью такого разделения облегчается понимание структуры программы.

Рисунок 1 – Разделение исходного кода
Описание файлов:
mwindows. h – здесь размещены объявление структур и функций используемых в программе.
mbutton. h и mbutton. c – программная реализация кнопок на экране, структуры и функции, поддерживающие их функциональность.
main. c – реализация главной функции
init. c – инициализации SDL, ресурсов, подготовка игрового поля
load. c – загрузка текстур в программу из растровых рисунков
coursePlayer. c – проверка хода игрока, обновление поля
mainLoop. c – игровой цикл, обработка событий, вывод изображения
pageWindow. c – инициализация кнопок в окне
computer_ai. c – функции хода компьютера
Основные функции
Функция, которая реализует размещение кораблей на поле.
В функции поочерёдно вызывается подфункция, которая ищет места для размещения. Размещение изначально задается случайно. Если место не подходит для размещения, то функция поочередно смешает позицию корабля в направлении вниз-вправо, если поле закончилось проверяются места в начале поля.
void spawnBoat(player* pl) { for(int i=0;i<10;++i) for(int j=0;j<10;++j) pl->field[i][j]=Wasser; //4, 3, 2 палубные корабли int k; for(int i=0;i<10;i++) { //определение длинны текущего корабля и размещение if(i==0) k=4; else if(i<3) k=3; else if(i<6) k=2; else k=1; if(boatHelp(&pl->field[0],k)) //если не удалось расставить корабли { for(int i=0;i<10;++i) for(int j=0;j<10;++j) pl->field[i][j]=Wasser; i=-1; } } for(int i=0;i<10;++i) for(int j=0;j<10;++j) if(pl->field[i][j]!=Boat) pl->field[i][j]=Wasser; } |
Листинг 1 – Функция расстановки всех кораблей
Функция, которая проверяет ход игрока
В коде функции проверяется указанное игроком поле и в зависимости от того что там находиться производится соответствующее действие.
int coursePlayer(char mass[10][10],int x, int y) //проверяет на попадание, передает ход, удаляет поля вокруг уничтоженного корабля { if(mass[y][x]==Wasser) { mass[y][x] = Not; if(res->xod==1) //передача хода res->xod = 2; else res->xod = 1; return 0; } if(mass[y][x]==Boat) { mass[y][x] = Kill; //проверка на полное уничтожение корабля //если уничтожен, то вокруг нельзя ставить корабли... int bcount =0; int kcount =1; char type =HORIZONT;// int begX, begY; //начальные кординаты размещения коробля begX = x;begY = y; //Определение начальных координат корабля и его длинны if(bcount>0)//корабль подбит не до конца { return 1; }else { //корабль подбит, нужно показать вокруг него мёртвую зону killBoat(mass, begX, begY, type, kcount); return 2; } } return 0; } |
Листинг 2 – Обработка хода игрока
Функция, которая выводит размещение кораблей на экран
Функция выводит графическое изображение в окне. Поле игрока состоит из тайтлов, квадратов с изображением. Эти тайтлы отображают текущий процесс игры.
void renderField() { #define size_title 48 SDL_Rect size;//расположение в текстуре SDL_Rect field;//рассположение на поле size. h = 80; size. w = 80; field. h = size_title; field. w = size_title; field. x = 20; field. y = 20; player*p = &res->p1; char ch; for(int i=0;i<10;i++) { for(int j = 0;j<10;j++) { ch = p->field[i][j]; if(res->lvl == lGame1vs1) { if(ch == Boat) { ch = Wasser; } } size. x = ch*80; //меняеться на расположение в текстуре size. y = 0; SDL_RenderCopy(gRenderer, res->titleset,&size,&field); field. x+=size_title; } field. x = 20; field. y+=size_title; } #define offset size_title*10 + 30 p = &res->p2; field. x = 20 + offset; field. y = 20; for(int i=0;i<10;i++) { for(int j = 0;j<10;j++) { ch = p->field[i][j]; if(ch == Boat) { ch = Wasser; } size. x = ch*80; //меняеться на расположение в текстуре size. y = 0; SDL_RenderCopy(gRenderer, res->titleset,&size,&field); field. x+=size_title; } field. x = 20 + offset; field. y+=size_title; } } |
Листинг 3 – Отображение поля
Функция, для выбора атакуемой координаты поля компьютером
Алгоритм компьютера заключается в ходах через клетку. В том случае если компьютер подбивает корабль, то он обстреливает клетки вокруг корабля, пока он не будет уничтожен.
void courseAI() { SDL_Delay(100); int r; int x, y; int count=0; if(res->compSTR. isAttack) { attackBoat(&y,&x); }else { if(res->compSTR. hp[1]) //2,3 - палубные корабли { r = 1+rand()%res->compSTR. hp[1];//range 1 -50 for(int i=0;i<10;i++) { for(int j=0;j<10;++j) { if(res->compSTR. field[i][j]==Wasser) { if(i%2==0&&j%2==0) { ++count; }else if(i%2==1&&j%2==1) { ++count; } if(count==r) { y = i; x = j; i=10; j=10; } } } } res->compSTR. hp[1]--; res->compSTR. hp[2]--; }else if(res->compSTR. hp[2])//1-палубные { r = 1+rand()%res->compSTR. hp[2];//range 1 -100 for(int i=0;i<10;i++) { for(int j=0;j<10;++j) { if(res->compSTR. field[i][j]==Wasser) { ++count; if(count==r) { y = i; x = j; i=10; j=10; } } } } res->compSTR. hp[2]--; } } int l = coursePlayer(res->p1.field, x,y); //этап добивания корабля if(l==1) { res->compSTR. field[y][x] = Kill; res->compSTR. isAttack = 1; res->compSTR. x = x; res->compSTR. y = y; } if(l==2) { //необходимо обновить поле компьтера //для того чтобы он не ходил по мертвым полям res->compSTR. x = x; res->compSTR. y = y; res->compSTR. field[y][x] = Kill; res->compSTR. isAttack = 0; updateBoard(); } if(l==0) res->compSTR. field[y][x] = Not; return; } |
Листинг 4 – Функция хода компьютера
3 Инструкция пользователя
После запуска программы можно увидеть меню выбора режима игры.

Рисунок 2 – Меню выбора
После выбора режима игры с компьютером игрок видит два поля. Левое поле – это поле с кораблями игрока. Правое поле – поле с кораблями противника.

Рисунок 3 – Начало игры
После того как игрок нажал на клетку поля, которую ранее он не атаковал, результат выводиться на экран в виде изменённой плитки. В случае промаха ход передается компьютеру. В случае попадания ход остается у игрока.

Рисунок 4 – Стрельба по полю
В том случае если игрок или компьютер полностью уничтожит корабль, то вокруг этого корабля появиться зона, в которую нельзя было поставить корабль, и поэтому атаковать туда бессмысленно.

Рисунок 5 – Обновление клеток вокруг сбитого корабля
Игра продолжается до тех пор, пока не будут найдены все корабли игрока или компьютера. В этом случае на экране отображается соответствующее сообщение об окончании игры.

Рисунок 6 – Окончание игры

Рисунок 7 – Конец игры
Заключение
Во время выполнения курсовой работы были изучены принципы программирования на языке Си, были освоены основные концепции и стратегии по созданию графического приложения «Морской бой», которое является результатом выполнения курсовой работы.
Библиографический список
1 зык программирования Си 2-е издание / Б. ичи М.:Издательский дом "Вильямс", 2012. 272 с.
2 Статья о алгоритмах в морском бое [Электронный ресурс]:
https://habrahabr. ru/post/180995/
3 Статьи про графическую библиотеку SDL [Электронный ресурс]:
http:///tutorials/SDL/index. php
Приложение А
Код программы
Файл main. c
#include "mwindow. h"
#include <SDL2/SDL. h>
#include <stdio. h>
#include <stdlib. h>
#include <time. h>
int main( int argc, char* args[] )
{
srand(time(NULL));
init();
loadMedia();
eventLoop();
close();
return 0;
}
Файл mwindows. h
#ifndef MWINDOW_H_INCLUDED
#define MWINDOW_H_INCLUDED
#include <stdio. h>
#include <SDL2/SDL. h>
#define WINDOW_COUNT 3
#define HORIZONT 0
#define VERTICAL 1
enum buttonName{bStart1vs1,bStart1vsComp, bExit};
enum levelMenu{lMenu, lGame1vs1,lGame1vsComp, lWin, lFail};
enum type_title{Wasser, Boat, Not, Kill, Anim};
typedef struct __Player_Field
{
char field[10][10];
int countBoats;//пока не 0 -> еще есть корабли
} player;
typedef struct __Copm_Strategy
{
/*
комп знает текущее кол-во различных типов
кораблей противника в зависимости
от этого выстраивает стратегию
*/
char field[10][10]; //проверка на битые корабли
char hp[3];
char isAttack; //подбит ли корабль
char x, y; //местоположение последнего верного удара
}CompSTR;
typedef struct __Resurse
{
int quit;
int winHeight;
1, лист 1
int winWidth;
enum levelMenu lvl;
int xod; //1 -1,_player 2 - 2_player
//Pags
//Menu
SDL_Texture *menu;//backround
SDL_Rect btnSize[3]; //размеры кнопок в текстуре
SDL_Rect btnField[3];//расположнение в окне
SDL_Texture *button;
//Field
SDL_Texture *field;
SDL_Texture *titleset;
SDL_Rect btnExitSize; //размеры кнопок в текстуре
SDL_Rect btnExitField;//расположнение в окне
//Win
SDL_Texture* win;
SDL_Texture* players;
//Fail
SDL_Texture* fail;
//player struct
player p1,p2;
CompSTR compSTR;
}Resurse;
void firstInit();
void init();
void initResurse();
void initPageMenu();
void initPageG1vs1();
void loadMedia();
void close();
void eventLoop();
void mouseEvent();
void render();
SDL_Texture* loadTexture(char* path);
void preGame();//подготовка к игре
void spawnBoat(player* pl); //размещение всех кораблей
int boatHelp(char mass[10][10],int n); //размещение одного корабля
void initAI(); //инициализация структуры аи
void courseAI(); //ход компьтера
int coursePlayer(char mass[10][10],int x, int y); //обработка хода
void killBoat(char mass[10][10],int f, int z, int or, int n);
//создание мертвой зоны вокруг корабля
void initPageMenu(void);
void initPageG1vs1(void);
#endif // MWINDOW_H_INCLUDED
1, лист 2
Файл mainLoop. c
#include "mwindow. h"
#include "mbutton. h"
static SDL_Rect rec1 = {0,0,100,20};
static SDL_Rect rec2 = {250,450,440,80};
extern SDL_Renderer *gRenderer;
extern Resurse *res;
extern SDL_Window *mwin;
extern stackWidget *stWidget[WINDOW_COUNT];
static SDL_Event event;
void WinFailEvent()//прогрыш анимации
{
//анимация окончания игры
if(res->lvl==lGame1vsComp||res->lvl==lGame1vs1)
if(res->p1.countBoats==0||res->p2.countBoats==0)
{
for(int i=0;i<10;i++)
{
for(int j =0;j<10;j++)
{
if(res->p1.countBoats==0)
{
res->p1.field[i][j] = Anim;
res->p1.field[i][j] = Anim;
}else
{
res->p2.field[i][j] = Anim;
res->p2.field[i][j] = Anim;
}
render();
SDL_Delay(50);
}
}
}
if(res->lvl==lGame1vs1)
{
if(res->p1.countBoats==0||res->p2.countBoats==0)
{
res->lvl = lWin;
}
}
if(res->lvl==lGame1vsComp)
{
if(res->p1.countBoats==0)
{
res->lvl = lFail;
}
if(res->p2.countBoats==0)
{
res->lvl = lWin;
}
}
if(res->lvl==lWin||res->lvl==lFail)
{
1, лист 2
render();
SDL_Delay(500);
while( SDL_PollEvent( &event ) != 0 );
SDL_Delay(500);
}
}
void eventLoop()
{
while( !res->quit )
{
while( SDL_PollEvent( &event ) != 0 )
{
switch (event. type)
{
case SDL_QUIT:
res->quit = 1;
break;
case SDL_KEYDOWN:
if(event. key. keysym. sym ==SDLK_ESCAPE)
if(res->lvl!=lMenu)
res->lvl=lMenu;
break;
case SDL_MOUSEBUTTONDOWN:
mouseEvent();
break;
default:
;
}
}
if(res->lvl==lGame1vsComp)
if(res->xod==2)
courseAI();
render();
if(res->lvl==lGame1vsComp||res->lvl==lGame1vs1)
WinFailEvent();
}
}
void mouseEvent()
{
if(res->lvl == lWin||res->lvl == lFail)
{
SDL_Delay(300);
res->lvl = lMenu;
}else
{
clickMouse(stWidget[res->lvl],event. button. x,event. button. y);
}
}
void renderField()
{
#define size_title 48
SDL_Rect size;//расположение в текстуре
SDL_Rect field;//рассположение на поле
size. h = 80;
size. w = 80;
field. h = size_title;
field. w = size_title;
1, лист 3
field. x = 20;
field. y = 20;
player*p = &res->p1;
char ch;
for(int i=0;i<10;i++)
{
for(int j = 0;j<10;j++)
{
ch = p->field[i][j];
if(res->lvl == lGame1vs1)
{
if(ch == Boat)
{
ch = Wasser;
}
}
size. x = ch*80; //меняеться на расположение в текстуре
size. y = 0;
SDL_RenderCopy(gRenderer, res->titleset,&size,&field);
field. x+=size_title;
}
field. x = 20;
field. y+=size_title;
}
#define offset size_title*10 + 30
p = &res->p2; //можно было выделить в отдельную функцию, но копипаст проще
field. x = 20 + offset;
field. y = 20;
for(int i=0;i<10;i++)
{
for(int j = 0;j<10;j++)
{
ch = p->field[i][j];
if(ch == Boat)
{
ch = Wasser;
}
size. x = ch*80; //меняеться на расположение в текстуре
size. y = 0;
SDL_RenderCopy(gRenderer, res->titleset,&size,&field);
field. x+=size_title;
}
field. x = 20 + offset;
field. y+=size_title;
}
}
void render()
{
SDL_RenderClear( gRenderer );
switch(res->lvl)
{
case lMenu:
SDL_RenderCopy( gRenderer, res->menu, NULL, NULL );//backround
for(int i = 0;i<3;i++)
1, лист 4
SDL_RenderCopy(gRenderer, res->button,&res->btnSize[i],&res->btnField[i]);//button
break;
case lGame1vs1: //поле для игры одно и то же
case lGame1vsComp:
SDL_RenderCopy( gRenderer, res->field, NULL, NULL );//backround
SDL_RenderCopy(gRenderer, res->button,&res->btnExitSize,&res->btnExitField);
renderField();
break;
case lWin:
if(res->p2.countBoats==0)
{
rec1.y=20;
}
SDL_RenderCopy( gRenderer, res->win, NULL, NULL );//backround
if(res->lvl==lGame1vs1)
SDL_RenderCopy(gRenderer, res->players, &rec1, &rec2);
break;
case lFail:
SDL_RenderCopy( gRenderer, res->fail, NULL, NULL );//backround
break;
default:
;
}
1, лист 5


