Федеральное агентство железнодорожного транспорта

Омский государственный университет путей сообщения

Кафедра «Автоматика и системы управления»

ПРОГРАММА «МОРСКОЙ БОЙ»

Пояснительная записка к курсовой работе

по дисциплине «Программирование»

ИНМВ.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