Разработка программы-игры "Шашки"

Тип работы:
Курсовая
Предмет:
Программирование


Узнать стоимость

Детальная информация о работе

Выдержка из работы

Задание на выполнение

Разработать приложение, представляющее собой программную реализацию известной логической игры «Шашки».

Игра идет на поле размерами 8 X 8 клеток, шашки занимают первые три ряда с каждой стороны, бить можно произвольное количество шашек в любых направлениях, простая может бить назад, дамка может ходить на любое число полей, цель игры съесть или запереть все шашки противника. Каждая из сторон в начале игры имеет по 12 шашек. Шашки расставляются в трех ближайших к игрокам, горизонталях. Два центральных ряда полей остаются свободными. Здесь на этих полях, происходит сближение и первое соприкосновение противостоящих сил. Противники ходят поочередно, перемещая шашки своего цвета по игровым полям. Первыми начинают белые. Выигрывает та сторона, которой удалось уничтожить или заблокировать движение всех шашек противника.

Введение

программа игра шашки

Концепция объектно-ориентированного программирования подразумевает, что основой управления процессом реализации программы является передача сообщений объектам. Поэтому объекты должны определяться совместно с сообщениями, на которые они должны реагировать при выполнении программы. В этом состоит главное отличие ООП от процедурного программирования, где отдельно определённые структуры данных передаются в процедуры (функции) в качестве параметров. Таким образом, объектно-ориентированная программа состоит из объектов — отдельных фрагментов кода, обрабатывающего данные, которые взаимодействуют друг с другом через определённые интерфейсы.

Цель работы: закрепление теоретических знаний, полученных при изучении курса «Объектно-ориентированное программирование», путем создания приложения, представляющего собой программную реализацию известной логической игры «Шашкии».

Разработка объектно-ориентированных программ состоит из следующих последовательных работ:

— определение основных объектов, необходимых для решения данной задачи;

— определение закрытых данных (данных состояния) для выбранных объектов;

— определение второстепенных объектов и их закрытых данных;

— определение иерархической системы классов, представляющих выбранные объекты;

— определение ключевых сообщений, которые должны обрабатывать объекты каждого класса;

— разработка последовательности выражений, которые позволяют решить поставленную задачу;

— разработка методов, обрабатывающих каждое сообщение;

— очистка проекта, то есть устранение всех вспомогательных промежуточных материалов, использовавшихся при проектировании;

— кодирование, отладка, компоновка и тестирование.

Объектно-ориентированное программирование позволяет программисту моделировать объекты определённой предметной области путем программирования их содержания и поведения в пределах класса. Конструкция «класс» обеспечивает механизм инкапсуляции для реализации абстрактных типов данных.

Инкапсуляция как бы скрывает и подробности внутренней реализации типов, и внешние операции и функции, допустимые для выполнения над объектами этого типа.

1. Объектно-ориентированное программирование

Объектно-ориентированное, или объектное, программирование (в дальнейшем ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов. В случае языков с прототипированием вместо классов используются объекты-прототипы.

1. 1 История возникновения

ООП возникло в результате развития идеологии процедурного программирования, где данные и подпрограммы (процедуры, функции) их обработки формально не связаны. Для дальнейшего развития объектно-ориентированного программирования часто большое значение имеют понятия события (так называемое событийно-ориентированное программирование) и компонента (компонентное программирование, КОП).

Формирование КОП от ООП произошло, как случилось формирование модульного от процедурного программирования: процедуры сформировались в модули — независимые части кода до уровня сборки программы, так объекты сформировались в компоненты — независимые части кода до уровня выполнения программы. Взаимодействие объектов происходит посредством сообщений. Результатом дальнейшего развития ООП, по-видимому, будет агентно-ориентированое программирование, где агенты — независимые части кода на уровне выполнения. Взаимодействие агентов происходит посредством изменения среды, в которой они находятся.

Языковые конструкции, конструктивно не относящиеся непосредственно к объектам, но сопутствующие им для их безопасной (исключительные ситуации, проверки) и эффективной работы, инкапсулируются от них в аспекты (в аспектно-ориентированном программировании). Субъектно-ориентированное программирование расширяет понятие объект посредством обеспечения более унифицированного и независимого взаимодействия объектов. Может являться переходной стадией между ООП и агентным программирование в части самостоятельного их взаимодействия.

Первым языком программирования, в котором были предложены принципы объектной ориентированности, была Симула. В момент своего появления (в 1967 году), этот язык программирования предложил поистине революционные идеи: объекты, классы, виртуальные методы и др., однако это всё не было воспринято современниками как нечто грандиозное. Тем не менее, большинство концепций были развиты Аланом Кэйем и Дэном Ингаллсом в языке Smalltalk. Именно он стал первым широко распространённым объектно-ориентированным языком программирования.

В настоящее время количество прикладных языков программирования (список языков), реализующих объектно-ориентированную парадигму, является наибольшим по отношению к другим парадигмам. В области системного программирования до сих пор применяется парадигма процедурного программирования, и общепринятым языком программирования является язык C. Хотя при взаимодействии системного и прикладного уровней операционных систем заметное влияние стали оказывать языки объектно-ориентированного программирования.

1. 2 Определение ООП и его основные концепции

программа объектный технический

Появление в ООП отдельного понятия класса закономерно вытекает из желания иметь множество объектов со сходным поведением. Класс в ООП — это в чистом виде абстрактный тип данных, создаваемый программистом. С этой точки зрения объекты являются значениями данного абстрактного типа, а определение класса задаёт внутреннюю структуру значений и набор операций, которые над этими значениями могут быть выполнены. Желательность иерархии классов (а значит, наследования) вытекает из требований к повторному использованию кода — если несколько классов имеют сходное поведение, нет смысла дублировать их описание, лучше выделить общую часть в общий родительский класс, а в описании самих этих классов оставить только различающиеся элементы.

Необходимость совместного использования объектов разных классов, способных обрабатывать однотипные сообщения, требует поддержки полиморфизма — возможности записывать разные объекты в переменные одного и того же типа. В таких условиях объект, отправляя сообщение, может не знать в точности, к какому классу относится адресат, и одни и те же сообщения, отправленные переменным одного типа, содержащим объекты разных классов, вызовут различную реакцию.

Отдельного пояснения требует понятие обмена сообщениями. Первоначально (например, в том же Smalltalk) взаимодействие объектов представлялось как «настоящий» обмен сообщениями, то есть пересылка от одного объекта другому специального объекта-сообщения. Такая модель является чрезвычайно общей. Она прекрасно подходит, например, для описания параллельных вычислений с помощью активных объектов, каждый из которых имеет собственный поток исполнения и работает одновременно с прочими. Такие объекты могут вести себя как отдельные, абсолютно автономные вычислительные единицы. Посылка сообщений естественным образом решает вопрос обработки сообщений объектами, присвоенными полиморфным переменным — независимо от того, как объявляется переменная, сообщение обрабатывает код класса, к которому относится присвоенный переменной объект.

Однако общность механизма обмена сообщениями имеет и другую сторону — «полноценная» передача сообщений требует дополнительных накладных расходов, что не всегда приемлемо. Поэтому в большинстве ныне существующих объектно-ориентированных языков программирования используется концепция «отправка сообщения как вызов метода» — объекты имеют доступные извне методы, вызовами которых и обеспечивается взаимодействие объектов. Данный подход реализован в огромном количестве языков программирования, в том числе C++, Object Pascal. В настоящий момент именно он является наиболее распространённым в объектно-ориентированных языках.

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

1. 3 Особенности реализации

Как уже говорилось выше, в современных объектно-ориентированных языках программирования каждый объект является значением, относящимся к определённому классу. Класс представляет собой объявленный программистом составной тип данных, имеющий в составе:

Параметры объекта (конечно, не все, а только необходимые в программе), задающие его состояние (свойства объекта предметной области). Иногда поля данных объекта называют свойствами объекта, из-за чего возможна путаница. Физически поля представляют собой значения (переменные, константы), объявленные как принадлежащие классу.

Процедуры и функции, связанные с классом. Они определяют действия, которые можно выполнять над объектом такого типа, и которые сам объект может выполнять.

Классы могут наследоваться друг от друга. Класс-потомок получает все поля и методы класса-родителя, но может дополнять их собственными либо переопределять уже имеющиеся. Большинство языков программирования поддерживает только единичное наследование (класс может иметь только один класс-родитель), лишь в некоторых допускается множественное наследование — порождение класса от двух или более классов-родителей. Множественное наследование создаёт целый ряд проблем, как логических, так и чисто реализационных, поэтому в полном объёме его поддержка не распространена. Вместо этого в 1990-е годы появилось и стало активно вводиться в объектно-ориентированные языки понятие интерфейса. Интерфейс — это класс без полей и без реализации, включающий только заголовки методов. Если некий класс наследует (или, как говорят, реализует) интерфейс, он должен реализовать все входящие в него методы. Использование интерфейсов предоставляет относительно дешёвую альтернативу множественному наследованию.

Взаимодействие объектов в абсолютном большинстве случаев обеспечивается вызовом ими методов друг друга.

Поскольку методы класса могут быть как чисто внутренними, обеспечивающими логику функционирования объекта, так и внешними, с помощью которых взаимодействуют объекты, необходимо обеспечить скрытость первых при доступности извне вторых. Для этого в языки вводятся специальные синтаксические конструкции, явно задающие область видимости каждого члена класса. Традиционно это модификаторы public, protected и private, обозначающие, соответственно, открытые члены класса, члены класса, доступные только из классов-потомков и скрытые, доступные только внутри класса. Конкретная номенклатура модификаторов и их точный смысл различаются в разных языках.

Поля класса, в общем случае, не должны быть доступны извне, поскольку такой доступ позволил бы произвольным образом менять внутреннее состояние объектов. Поэтому поля обычно объявляются скрытыми (либо язык в принципе не позволяет обращаться к полям класса извне), а для доступа к находящимся в полях данным используются специальные методы, называемые методами доступа. Такие методы либо возвращают значение того или иного поля, либо производят запись в это поле нового значения. При записи метод доступа может проконтролировать допустимость записываемого значения и, при необходимости, произвести другие манипуляции с данными объекта, чтобы они остались корректными (внутренне согласованными). Методы доступа называют ещё аксессорами (от англ. access — доступ), а по отдельности — геттерами (англ. get — чтение) и сеттерами.

Псевдополя, доступные для чтения и / или записи. Свойства внешне выглядят как поля и используются аналогично доступным полям (с некоторыми исключениями), однако фактически при обращении к ним происходит вызов методов доступа. Таким образом, свойства можно рассматривать как «умные» поля данных, сопровождающие доступ к внутренним данным объекта какими-либо дополнительными действиями (например, когда изменение координаты объекта сопровождается его перерисовкой на новом месте). Свойства, по сути — не более чем синтаксический сахар, поскольку никаких новых возможностей они не добавляют, а лишь скрывают вызов методов доступа. Конкретная языковая реализация свойств может быть разной. Например, в C#объявление свойства непосредственно содержит код методов доступа, который вызывается только при работе со свойствами, то есть не требует отдельных методов доступа, доступных для непосредственного вызова. В Delphi объявление свойства содержит лишь имена методов доступа, которые должны вызываться при обращении к полю. Сами методы доступа представляют собой обычные методы с некоторыми дополнительными требованиями к сигнатуре.

Полиморфизм реализуется путём введения в язык правил, согласно которым переменной типа «класс» может быть присвоен объект любого класса-потомка её класса.

1. 4 Основные понятия

Абстрагирование — это способ выделить набор значимых характеристик объекта, исключая из рассмотрения незначимые. Соответственно, абстракция — это набор всех таких характеристик.

Инкапсуляция — это свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе и скрыть детали реализации от пользователя.

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

Полиморфизм — это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.

Класс является описываемой на языке терминологии (пространства имён) исходного кода моделью ещё не существующей сущности (объекта). Фактически он описывает устройство объекта, являясь своего рода чертежом. Говорят, что объект — это экземпляр класса. При этом в некоторых исполняющих системах класс также может представляться некоторым объектом при выполнении программы посредством динамической идентификации типа данных. Обычно классы разрабатывают таким образом, чтобы их объекты соответствовали объектам предметной области.

Объект

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

Прототип — это объект-образец, по образу и подобию которого создаются другие объекты. Объекты-копии могут сохранять связь с родительским объектом, автоматически наследуя изменения в прототипе; эта особенность определяется в рамках конкретного языка.

2. Логическая игра «Шашки»

2.1 Назначение программы

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

2.2 Используемые технические и программные средства

Программа была создана в среде программирования Borland Delphi 7.0.

Язык программирования: Pascal

Минимальные системные требования:

— не менее 500 кб свободного места на жёстком диске;

— не менее 4 Мб свободной оперативной памяти;

— операционная система — Windows XP/2000/Vista;

— клавиатура;

— мышь.

Порядок запуска программы на выполнение:

Для запуска программы достаточно запустить (сделать на нём двойной щелчок левой клавишей мыши) файл DCheckers. exe.

Программа в систему не устанавливается, после использования может быть просто удалена с жёсткого диска компьютера пользователя.

2.3 Описание логической структуры

Исходный код состоит из четырех unit (Main, Draughts, About, VChessBoard) и двух форм (fmCheckers, fmAbout).

unit VChessBoard — данный раздел содержит метод прорисовки поля (прорисовка клеток и шашек).

unit About — данный раздел содержит информацию о программе и правила игры;

unit Draughts — данный раздел описывает все действия, совершаемые с шашками (Возможные ходы, запись ходов, рубка, запоминание срубленных шашек)

unit Main — данный раздел описывает алгоритм игры. Если шашка выбрана, то проверяем правильность ходов, если рубки нет и клетка пустая, то ход сделан и переключаем игрока, если есть возможность рубки, то рубить обязательною. Отрисовка возможных ходов с учетом обязательной рубки. Проверяем, возможна ли рубка в данную клетку, рубка верна, далее рубим, определяем срубленную шашку и перескакиваем. Далее мы определяем, возможно, ли продолжение рубки, устанавливаем режим рубки, заканчиваем рубку и переключаем игрока. Проверяем победу по срубам и по блокировкам. Заканчиваем игру, и всплывает окно: победа команды белых или черных. Так же показывается, чей ход и время игры.

2.4 Результаты тестирования

Исходный вид программы, после запуска приложения появится окно (рис. 1):

Рисунок 1 — Основная форма содержащая игровое поле

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

Рисунок 2 — Раздел меню, начать игру.

Нажав на начав игру, на игровом поле появятся шашки. Белые и черные. Ход начинают белые. С запуском начинается отсчет времени игры (рис. 3)

Рисунок 3 — Появление шашек. Время игры.

Когда мы выбираем шашку, появляются возможные ходы (рис. 4) или возможность рубки (рис. 5).

Простые шашки могут перемещаться только вперед по диагонали на одну клетку, если она не занята. Если клетка занята шашкой соперника и за этой клеткой свободное поле, тогда должен быть произведен бой — шашка ходящего «перескакивает» через шашку соперника и последняя снимается с доски. За один бой простая шашка может побить несколько шашек соперника.

Рисунок 4 — Возможные ходы

Рисунок 5 — Возможность рубки

При достижении простой последней горизонтали шашка становится дамкой (рис. 6).

Рисунок 6 — Дамка

Дамки могут ходить (рис. 7) или совершать бой по диагонали в любом направлении (рис. 8)

Рисунок 7 — Возможные ходы дамки

Рисунок 8 — Возможность рубки дамкой

После того как какой либо из игроков выигрывает всплывает окно: Игра закончена (рис. 9) и какая команда победила (рис. 10)

Рисунок 9 — Окно: Игра закончена

Рисунок 10 — Окно, какая команда победила

Нажав кнопку «О программе», пользователь увидит правила игры (рис. 11).

Рисунок 11 — О программе

Чтобы выйти из приложения в меню нужно выбрать файл и нажать на выход (рис. 7).

Рисунок 7 — Выход из программы

2.6 Инструкция

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

Заключение

В рамках данного курсового проекта было разработано программное обеспечение, реализующее собой игру «Шашки». В ходе написания проекта была освоена и закреплена работа с классами и объектами.

С помощью разработанной программы пользователи могут овладеть искусством игры в «Шашки», а так же развить свое логическое мышление.

Пользовательское приложение разработано в среде программирования Borland Delphi 7.0.

Список литературы

1) Галисеев Г. В. Программирование в среде Delphi 2005. Самоучитель. Издательство «Диалектика», 2006.

2) Бобровский С. И. Технологии Delphi. Разработка приложений для бизнеса. Учебный курс. Издательство «Питер», 2006.

3) Котов В. М. Чрезвычайно грамотное и подробное описание задач динамического программирования, 2000

4) Фаронов В. В. Delphi. Программирование на языке высокого уровня. Учебник для вузов. Издательство «Питер», 2006.

5) Цветкова А. В. Информатика и информационные технологии. Издательство «Эксмо», 2008.

6) Специалисты NIIT. Использование C#. Специальное издание.: Перевод с англ. — М.: Издательский дом «Вильямс», 2002. — 528 с.

Приложение А

Исходный код

unit Main;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, ExtCtrls, StdCtrls, Buttons, Menus,

Draughts, VChessBoard;

type

TfmCheckers = class (TForm)

Timer1: TTimer;

pnRight: TPanel;

MainMenu1: TMainMenu;

miFile: TMenuItem;

miGame: TMenuItem;

miHelp: TMenuItem;

miAbout: TMenuItem;

miExit: TMenuItem;

miStartLocalGame: TMenuItem;

pnChessBoard: TPanel;

lbedBlackName: TLabeledEdit;

lbedWhiteName: TLabeledEdit;

Label1: TLabel;

Label2: TLabel;

stCurrentPlayer: TStaticText;

Label3: TLabel;

stTimer: TStaticText;

miCancelGame: TMenuItem;

procedure miExitClick (Sender: TObject);

procedure FormCreate (Sender: TObject);

procedure FormResize (Sender: TObject);

procedure miStartLocalGameClick (Sender: TObject);

procedure Timer1Timer (Sender: TObject);

procedure miCancelGameClick (Sender: TObject);

procedure miAboutClick (Sender: TObject);

private

{Private declarations}

public

{Public declarations}

vcbChess: TVisualChessBoard;

PlayerSide, CurrentPlayer: Integer;

IsDraughtChosen: Boolean;

DraughtPosition: TCellPosition;

StrikeMode: Boolean;

PlayTime: Integer;

StartTime: TDateTime;

LocalGame: Boolean;

procedure vcbChessMouseMove (Sender: TObject; Shift: TShiftState; X, Y: Integer);

procedure vcbChessMouseDown (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

procedure vcbChessMouseUp (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

Procedure TrySelectNewDraught (x, y: Integer);

Procedure TryMakeStrike (x, y: Integer);

Procedure SwitchCurrentPlayer;

Function CheckForWin: Integer;

end;

var

fmCheckers: TfmCheckers;

implementation

uses About;

{$R *. dfm}

procedure TfmCheckers. miExitClick (Sender: TObject);

begin

Close;

end;

procedure TfmCheckers. FormCreate (Sender: TObject);

begin

vcbChess: =TVisualChessBoard. Create (pnChessBoard);

vcbChess. Parent: =pnChessBoard;

vcbChess. Left: =8;

vcbChess. Top: =8;

vcbChess. Width: =1200;

vcbChess. Height: =1200;

vcbChess. OnMouseMove: =vcbChessMouseMove;

vcbChess. OnMouseDown: =vcbChessMouseDown;

vcbChess. OnMouseUp: =vcbChessMouseUp;

vcbChess. Refresh;

end;

procedure TfmCheckers. FormResize (Sender: TObject);

Var

CellSize: Integer;

begin

If pnChessBoard. Width< pnChessBoard. Height Then

CellSize: =(pnChessBoard. Width-16) div 8

Else

CellSize: =(pnChessBoard. Height-16) div 8;

vcbChess. Left: =8;

vcbChess. Top: =8;

vcbChess. Width: =CellSize*8;

vcbChess. Height: =CellSize*8;

vcbChess. Refresh;

If IsDraughtChosen Then

TrySelectNewDraught (DraughtPosition. X, DraughtPosition. Y);

end;

procedure TfmCheckers. vcbChessMouseMove (Sender: TObject; Shift: TShiftState;

X, Y: Integer);

Var

xP, yP: Integer;

begin

vcbChess. Cursor: =crDefault;

vcbChess. GetCellNumberByCoord (X, Y, xP, yP);

If vcbChess. Board. GetDraught (xP, yP)< >nil Then

Begin

If vcbChess. Board. GetDraught (xP, yP). Side=PlayerSide Then

vcbChess. Cursor: =crHandPoint

End;

end;

procedure TfmCheckers. vcbChessMouseDown (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

Var

xP, yP: Integer;

begin

If StrikeMode Then Exit;

vcbChess. GetCellNumberByCoord (X, Y, xP, yP);

If Not (IsDraughtChosen) Then // Если еще нет выбранной шашки

Begin

TrySelectNewDraught (xP, yP);

End

Else Begin // Если уже есть выбранная шашка

If vcbChess. Board. GetDraught (xP, yP)=nil Then

Begin // Если клетка пустая, то обработаем как отпускание мыши

vcbChessMouseUp (Sender, Button, Shift, X, Y);

End

Else Begin // Если там стоит наша шашка, то сбросим выбор и рекурсируем

If vcbChess. Board. GetDraught (xP, yP). Side=PlayerSide Then

Begin

vcbChess. Refresh;

IsDraughtChosen: =False;

vcbChessMouseDown (Sender, Button, Shift, X, Y);

End;

End;

End;

end;

procedure TfmCheckers. vcbChessMouseUp (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

Var

xP, yP: Integer;

Moves, Strikes, Strickens: TList;

begin

vcbChess. GetCellNumberByCoord (X, Y, xP, yP);

If IsDraughtChosen Then // Если есть выбранная шашка

Begin

// Проверим: можно ли в принципе шагнуть сюда

If vcbChess. Board. GetDraught (xP, yP)< >nil Then Exit;

// Клетка пустая

// Проверим правильность хода в эту клетку

vcbChess. Board. FindMoves (DraughtPosition. X, DraughtPosition. Y, Moves, Strikes, Strickens);

If Strikes. Count=0 Then // Нет рубки — пытаемся просто ходить

Begin

If CheckPosition (xP, yP, Moves)> =0 Then // Ход верен!

Begin

vcbChess. Board. MoveDraught (DraughtPosition. X, DraughtPosition. Y, xP, yP);

IsDraughtChosen: =False;

vcbChess. Refresh;

vcbChessMouseMove (Sender, Shift, X, Y);

SwitchCurrentPlayer; // Ход сделан — переключем игрока

CheckForWin;

End;

End

Else Begin // Есть возможность рубки — обязательно!

TryMakeStrike (xP, yP);

End;

ClearMoves (Moves);

ClearMoves (Strikes);

ClearMoves (Strickens);

End

Else Begin // Шашка не еще выбрана

End;

end;

Procedure TfmCheckers. TrySelectNewDraught (x, y: Integer);

Var

Moves, Strikes, Strickens: TList;

StrikingDraughts: TList;

Begin

If vcbChess. Board. GetDraught (x, y)=nil Then Exit;

If vcbChess. Board. GetDraught (x, y). Side<>PlayerSide Then Exit;

// Проверим обязательные рубки

StrikingDraughts: =vcbChess. Board. FindStrikingDraughts (PlayerSide);

If StrikingDraughts. Count>0 Then

Begin

If CheckPosition (x, y, StrikingDraughts)<0 Then

Begin

ClearMoves (StrikingDraughts);

Exit;

End;

End;

ClearMoves (StrikingDraughts);

DraughtPosition. X:=x;

DraughtPosition. Y:=y;

IsDraughtChosen: =True;

// Отрисуем возможные ходы с учетом обязаловки рубки

vcbChess. Board. FindMoves (x, y, Moves, Strikes, Strickens);

If Strikes. Count=0 Then

vcbChess. DrawMoves (Moves, nil)

Else

vcbChess. DrawMoves (nil, Strikes);

ClearMoves (Moves);

ClearMoves (Strikes);

ClearMoves (Strickens);

End;

Procedure TfmCheckers. TryMakeStrike (x, y: Integer);

Var

Moves, Strikes, Strickens: TList;

CP: PCellPosition;

Begin

If Not (IsDraughtChosen) Then Exit;

vcbChess. Board. FindMoves (DraughtPosition. X, DraughtPosition. Y, Moves, Strikes, Strickens);

// Возможна ли рубка в данную клетку

If CheckPosition (x, y, Strikes)> =0 Then // Рубка верна!

Begin // рубим

// Определим срубленную шашку

CP: =Strickens. Items [CheckPosition (x, y, Strikes)];

vcbChess. Board. DeleteDraught (CP^. X, CP^. Y);

// Перескакиваем

vcbChess. Board. MoveDraught (DraughtPosition. X, DraughtPosition. Y, x, y);

DraughtPosition. X:=x; DraughtPosition. Y:=y;

ClearMoves (Moves);

ClearMoves (Strikes);

ClearMoves (Strickens);

// Определим: возможно ли продолжение рубки

vcbChess. Board. FindMoves (DraughtPosition. X, DraughtPosition. Y, Moves, Strikes, Strickens);

If Strikes. Count>0 Then

Begin

StrikeMode: =True; // Установим режим рубки

End

Else Begin

StrikeMode: =False;

SwitchCurrentPlayer; // Закончена рубка — переключим игрока

End;

ClearMoves (Moves);

ClearMoves (Strikes);

ClearMoves (Strickens);

vcbChess. Refresh;

End;

If StrikeMode Then

Begin

IsDraughtChosen: =False;

TrySelectNewDraught (DraughtPosition. X, DraughtPosition. Y);

End;

CheckForWin;

End;

Procedure TfmCheckers. SwitchCurrentPlayer;

Begin

If CurrentPlayer=sdWhite Then CurrentPlayer: =sdBlack Else CurrentPlayer: =sdWhite;

If LocalGame Then PlayerSide: =CurrentPlayer;

If CurrentPlayer=sdWhite Then stCurrentPlayer. Caption: ='Белые' Else stCurrentPlayer. Caption: ='Черные';

If PlayerSide=CurrentPlayer Then

Begin

pnChessBoard. Enabled: =True;

End

Else Begin

pnChessBoard. Enabled: =False;

End;

End;

Function TfmCheckers. CheckForWin: Integer;

Begin

Result: =-1;

// Проверим победу по срубам

If vcbChess. Board. CountDraughts (sdWhite)=0 Then

Begin

Result: =sdBlack;

End;

If vcbChess. Board. CountDraughts (sdBlack)=0 Then

Begin

Result: =sdWhite;

End;

// Проверим победу по блокировкам

If Not (vcbChess. Board. AbleToMove (CurrentPlayer)) Then

If CurrentPlayer=sdWhite Then Result: =sdBlack Else Result: =sdWhite;

If Result<0 Then Exit;

//Game Over!

miCancelGameClick (Self);

Case Result Of

sdWhite: Application. MessageBox ('Победила команда «Белых» ', 'Победили Белые!', MB_OK+MB_ICONINFORMATION);

sdBlack: Application. MessageBox ('Победила команда «Черных» ', 'Победили Черные!', MB_OK+MB_ICONINFORMATION);

End;

End;

procedure TfmCheckers. Timer1Timer (Sender: TObject);

begin

// Inc (PlayTime);

// stTimer. Caption: =Format (' % d:%. 2d:%. 2d', [PlayTime div 3600, (PlayTime mod 3600) div 60, (PlayTime mod 3600) mod 60]);

stTimer. Caption: =TimeToStr (Now-StartTime);

end;

procedure TfmCheckers. miStartLocalGameClick (Sender: TObject);

begin

LocalGame: =True;

lbedWhiteName. ReadOnly: =True;

lbedBlackName. ReadOnly: =True;

// PlayTime: =0;

StartTime: =Now;

PlayerSide: =sdWhite;

CurrentPlayer: =sdBlack;

IsDraughtChosen: =False;

StrikeMode: =False;

vcbChess. Board. Respawn;

vcbChess. Refresh;

pnChessBoard. Enabled: =True;

Timer1. Enabled: =True;

SwitchCurrentPlayer;

end;

procedure TfmCheckers. miCancelGameClick (Sender: TObject);

begin

Timer1. Enabled: =False;

pnChessBoard. Enabled: =False;

lbedWhiteName. ReadOnly: =False;

lbedBlackName. ReadOnly: =False;

Application. MessageBox ('Игра зкончена', 'Игра закончена', MB_OK+MB_ICONINFORMATION);

end;

procedure TfmCheckers. miAboutClick (Sender: TObject);

begin

fmAbout. ShowModal;

end;

end.

2

Unit Draughts;

interface

Uses

Classes;

Type

TMove=Record

dX, dY: Integer;

End;

Const

ctWhite=0;

ctBlack=1;

ctNone=255;

sdWhite=0;

sdBlack=1;

kdSimple=0;

kdKing=1;

MoveNW=1; {}

MoveNE=2; {}

MoveSW=3; {}

MoveSE=4; {}

DraughtMoves: Array [1. 4] Of TMove=((dX: — 1; dY: 1), (dX: 1; dY: 1), (dX: — 1; dY: — 1), (dX: 1; dY: — 1));

Type

TPossibleMoves=Set Of MoveNW. MoveSE;

PCellPosition=^TCellPosition;

TCellPosition=Record

X, Y: Integer;

End;

TDraught=Class (TObject)

Private

_Side: Integer; {Сторона: Белая / Черная}

_Kind: Integer; {Тип: Простая / Дамка}

PossibleMoves: TPossibleMoves; {Возможные ходы}

Procedure SetSide (Value: Integer);

Procedure SetKind (Value: Integer);

Public

Constructor Create (sd: Integer);

Destructor Destroy; Override;

Property Side: Integer read _Side write SetSide;

Property Kind: Integer read _Kind write SetKind;

End;

TBoardCell=Record

CellType: Integer;

Draught: TDraught;

End;

TChessBoard=Class (TObject)

Private

Cells: Array [0. 9, 0. 9] Of TBoardCell;

Public

Constructor Create;

Destructor Destroy; Override;

Function AddDraught (x, y: Integer; Side: Integer; Kind: Integer=kdSimple): TDraught;

Procedure DeleteDraught (x, y: Integer);

Function GetDraught (x, y: Integer): TDraught;

Function SetDraught (x, y: Integer; Const Draught: TDraught):Boolean;

Procedure Clear;

Procedure Respawn;

Function MoveDraught (FromX, FromY, ToX, ToY: Integer): Boolean;

Procedure FindMoves (x, y: Integer; Var Moves, Strikes, Strickens: TList);

Function FindStrikingDraughts (Side: Integer): TList;

Function CountDraughts (Side: Integer): Integer;

Function AbleToMove (Side: Integer): Boolean;

End;

Procedure ClearMoves (Var Moves: TList);

Function CheckPosition (x, y: Integer; Const Positions: TList):Integer;

implementation

Procedure ClearMoves (Var Moves: TList);

Var

i: Integer;

CP: PCellPosition;

Begin

If Moves=nil Then Exit;

For i: =0 To Moves. Count-1 Do

Begin

CP: =Moves. Items[i];

Dispose (CP);

End;

Moves. Free;

Moves: =nil;

End;

Function CheckPosition (x, y: Integer; Const Positions: TList):Integer;

Var

i: Integer;

CP: PCellPosition;

Begin

Result: =-1;

If Positions=nil Then Exit;

For i: =0 To Positions. Count-1 Do

Begin

CP: =Positions. Items[i];

If ((CP^. X=x) And (CP^. Y=y)) Then

Begin

Result: =i;

Exit;

End;

End;

End;

Constructor TDraught. Create (sd: Integer);

Begin

Inherited Create;

Side: =sd;

Kind: =kdSimple;

End;

Destructor TDraught. Destroy;

Begin

Inherited Destroy;

End;

Procedure TDraught. SetSide (Value: Integer);

Begin

If Value=sdWhite Then

Begin

_Side: =sdWhite;

PossibleMoves: =[MoveNW, MoveNE]

End

Else Begin

_Side: =sdBlack;

PossibleMoves: =[MoveSW, MoveSE];

End;

If Kind=kdKing Then PossibleMoves: =[MoveNW, MoveNE, MoveSW, MoveSE];

End;

Procedure TDraught. SetKind (Value: Integer);

Begin

If Value=kdSimple Then

_Kind: =kdSimple

Else

_Kind: =kdKing;

SetSide (_Side);

End;

Constructor TChessBoard. Create;

Var

x, y: Integer;

Begin

Inherited Create;

For y: =0 To 9 Do

Begin

For x: =0 To 9 Do

Begin

If ((x=0) Or (y=0) Or (x=9) Or (y=9)) Then

Cells [y, x]. CellType:=ctNone

Else

If ((Odd (x) And Odd (y)) Or (Not (Odd (x)) And Not (Odd (y)))) Then

Cells [y, x]. CellType:=ctBlack

Else

Cells [y, x]. CellType:=ctWhite;

Cells [y, x]. Draught:=nil;

End;

End;

End;

Destructor TChessBoard. Destroy;

Begin

Clear;

Inherited Destroy;

End;

Function TChessBoard. AddDraught (x, y: Integer; Side: Integer; Kind: Integer=kdSimple): TDraught;

Begin

Result: =nil;

If ((Cells [y, x]. CellType=ctNone) Or (Cells [y, x]. Draught<>nil)) Then Exit;

Result: =TDraught. Create (Side);

Result. Kind: =Kind;

Cells [y, x]. Draught:=Result;

End;

Procedure TChessBoard. DeleteDraught (x, y: Integer);

Begin

If Cells [y, x]. Draught<>nil Then Cells [y, x]. Draught. Free;

Cells [y, x]. Draught:=nil;

End;

Function TChessBoard. GetDraught (x, y: Integer): TDraught;

Begin

Result: =Cells [y, x]. Draught;

End;

Function TChessBoard. SetDraught (x, y: Integer; Const Draught: TDraught):Boolean;

Begin

Result: =True;

Cells [y, x]. Draught:=Draught;

If Draught=nil Then Exit;

If (((Draught. Side=sdWhite) And (y=8)) Or ((Draught. Side=sdBlack) And (y=1))) Then

Draught. Kind: =kdKing;

End;

Procedure TChessBoard. Clear;

Var

x, y: Integer;

Begin

For y: =0 To 9 Do

For x: =0 To 9 Do

DeleteDraught (x, y);

End;

Procedure TChessBoard. Respawn;

Var

x: Integer;

Begin

Clear;

// Белые

For x: =1 To 8 Do

Begin

If Odd (x) Then

AddDraught (x, 2, sdWhite)

Else Begin

AddDraught (x, 1, sdWhite);

AddDraught (x, 3, sdWhite);

End

End;

// Черные

For x: =1 To 8 Do

Begin

If Not (Odd (x)) Then

AddDraught (x, 7, sdBlack)

Else Begin

AddDraught (x, 8, sdBlack);

AddDraught (x, 6, sdBlack);

End

End;

End;

Function TChessBoard. MoveDraught (FromX, FromY, ToX, ToY: Integer): Boolean;

Begin

Result: =False;

If ((Cells [FromY, FromX]. CellType=ctNone) Or (Cells [ToY, ToX]. CellType=ctNone)) Then Exit;

If ((GetDraught (FromX, FromY)=nil) Or (GetDraught (ToX, ToY)< >nil)) Then Exit;

SetDraught (ToX, ToY, GetDraught (FromX, FromY));

SetDraught (FromX, FromY, nil);

Result: =True;

End;

Procedure TChessBoard. FindMoves (x, y: Integer; Var Moves, Strikes, Strickens: TList);

Var

i: Integer;

dX, dY, dL: Integer;

P: PCellPosition;

MoveOver: Boolean;

Begin

Moves: =nil;

Strikes: =nil;

Strickens: =nil;

If Cells [y, x]. Draught=nil Then Exit;

Moves: =TList. Create;

Strikes: =TList. Create;

Strickens: =TList. Create;

For i: =1 To 4 Do

Begin

MoveOver: =False;

dX: =DraughtMoves[i]. dX;

dY: =DraughtMoves[i]. dY;

dL: =0;

Repeat

Inc (dL);

If Cells [y+dY*dL, x+dX*dL]. CellType<>ctNone Then // Можно ли вставать на эту клетку

Begin

If Cells [y+dY*dL, x+dX*dL]. Draught=nil Then // Если клетка пустая, то запишем ход…

Begin

// …запишем ход в том случае, если шашка может ходить в эту сторону

If (i In Cells [y, x]. Draught. PossibleMoves) Then

Begin

New (P);

P^. X:=x+dX*dL; P^. Y:=y+dY*dL;

Moves. Add (P);

If Cells [y, x]. Draught. Kind=kdSimple Then MoveOver: =True; // Если это простая шашка, то закончим ход

End

Else // Шашка не может ходить в этом направлении — закончим ход

MoveOver: =True;

End

Else Begin // Если в клетке стоит шашка, то проверим ее

If Cells [y+dY*dL, x+dX*dL]. Draught. Side< >Cells [y, x]. Draught. Side Then

Begin // Если это чужая шашка, то проверим на сруб

Inc (dL); // Перешагнем через шашку

If ((Cells [y+dY*dL, x+dX*dL]. CellType<>ctNone) And (Cells [y+dY*dL, x+dX*dL]. Draught=nil)) Then // Можно ли вставать на эту клетку

Begin // Рубка удалась

// Запомним срубленную шашку

New (P);

P^. X:=x+dX*(dL-1); P^. Y:=y+dY*(dL-1);

Strickens. Add (P);

// Запишем рубку

New (P);

P^. X:=x+dX*dL; P^. Y:=y+dY*dL;

Strikes. Add (P);

End;

End;

MoveOver: =True;

End;

End

Else // Достигнут бордюр — завершим ход

MoveOver: =True;

Until MoveOver;

End;

End;

Function TChessBoard. FindStrikingDraughts (Side: Integer): TList;

Var

x, y: Integer;

Moves, Strikes, Strickens: TList;

CP: PCellPosition;

Begin

Result: =TList. Create;

For y: =1 To 8 Do

For x: =1 To 8 Do

If GetDraught (x, y)< >nil Then

If GetDraught (x, y). Side=Side Then

Begin

FindMoves (x, y, Moves, Strikes, Strickens);

If Strikes. Count>0 Then

Begin

New (CP);

CP^. X:=x; CP^. Y:=y;

Result. Add (CP);

End;

ClearMoves (Moves);

ClearMoves (Strikes);

ClearMoves (Strickens);

End;

End;

Function TChessBoard. CountDraughts (Side: Integer): Integer;

Var

x, y: Integer;

Begin

Result: =0;

For y: =1 To 8 Do

For x: =1 To 8 Do

If GetDraught (x, y)< >nil Then

If GetDraught (x, y). Side=Side Then Inc (Result);

End;

Function TChessBoard. AbleToMove (Side: Integer): Boolean;

Var

x, y: Integer;

Moves, Strikes, Strickens: TList;

Begin

Result: =False;

For y: =1 To 8 Do

For x: =1 To 8 Do

If GetDraught (x, y)< >nil Then

If GetDraught (x, y). Side=Side Then

Begin

FindMoves (x, y, Moves, Strikes, Strickens);

If ((Moves. Count> 0) Or (Strikes. Count> 0)) Then Result: =True;

ClearMoves (Moves);

ClearMoves (Strikes);

ClearMoves (Strickens);

If Result Then Exit;

End;

End;

end.

unit VChessBoard;

interface

Uses

Types, Classes, ExtCtrls,

Draughts;

Type

TVisualChessBoard=Class (TImage)

Private

Procedure DrawDraught (x, y: Integer; Side: Integer; Kind: Integer);

Procedure DrawMove (x, y: Integer);

Procedure DrawStrike (x, y: Integer);

Public

Board: TChessBoard;

Constructor Create (AOwner: TComponent); Override;

Destructor Destroy; Override;

Procedure Repaint; Override;

Function GetCellRectByNumber (x, y: Integer): TRect;

Procedure GetCellNumberByCoord (xCoord, yCoord: Integer; Var x, y: Integer);

Procedure DrawMoves (Const Moves, Strikes: TList);

Procedure DrawMovesByNumber (x, y: Integer);

End;

implementation

Uses

Graphics;

Constructor TVisualChessBoard. Create (AOwner: TComponent);

Begin

Inherited Create (AOwner);

Board: =TChessBoard. Create;

// Repaint;

End;

Destructor TVisualChessBoard. Destroy;

Begin

Board. Free;

Inherited Destroy;

End;

Procedure TVisualChessBoard. Repaint;

Var

x, y: Integer;

Begin

Inherited Repaint;

{Нарисуем клетки}

Canvas. Brush. Style: =bsSolid;

For y: =1 To 8 Do

Begin

For x: =1 To 8 Do

Begin

If ((Odd (x) And Odd (y)) Or (Not (Odd (x)) And Not (Odd (y)))) Then

Canvas. Brush. Color: =clBlack

Else

Canvas. Brush. Color: =clWhite;

Canvas. FillRect (GetCellRectByNumber (x, y));

End;

End;

Canvas. Pen. Color: =clBlack;

Canvas. Pen. Style: =psSolid;

Canvas. Pen. Width: =1;

Canvas. Brush. Style: =bsClear;

// Canvas. Rectangle (0, (Height mod 8), Width — (Width mod 8), Height);

Canvas. Rectangle (0, 0, Width, Height);

{Нарисуем шашки}

If Board=nil Then Exit;

For y: =1 To 8 Do

Begin

For x: =1 To 8 Do

Begin

If Board. GetDraught (x, y)< >nil Then

DrawDraught (x, y, Board. GetDraught (x, y). Side, Board. GetDraught (x, y). Kind);

End;

End;

End;

Function TVisualChessBoard. GetCellRectByNumber (x, y: Integer): TRect;

Begin

Result. Left: =Trunc ((x-1)*(Width/8));

Result. Right: =Trunc (x*(Width/8));

Result. Top: =Height-Trunc (y*(Height/8));

Result. Bottom: =Height-Trunc ((y-1)*(Height/8));

End;

Procedure TVisualChessBoard. GetCellNumberByCoord (xCoord, yCoord: Integer; Var x, y: Integer);

Begin

x: =1+Trunc (xCoord/(Width/8));

y: =8-Trunc (yCoord/(Height/8));

End;

Procedure TVisualChessBoard. DrawDraught (x, y: Integer; Side: Integer; Kind: Integer);

Const

dR=5;

Var

R, tR: TRect;

Begin

Canvas. Pen. Color: =clSilver;

Canvas. Pen. Style: =psSolid;

Canvas. Pen. Width: =3;

Canvas. Brush. Style: =bsSolid;

If Side=sdWhite Then Canvas. Brush. Color: =clWhite Else Canvas. Brush. Color: =clBlack;

R: =GetCellRectByNumber (x, y);

Canvas. Ellipse (R. Left+dR, R. Top+dR, R. Right-dR, R. Bottom-dR);

If Kind=kdKing Then Begin

tR: =R;

R. Left: =tR. Left+((tR. Right-tR. Left) div 4);

R. Right: =tR. Right — ((tR. Right-tR. Left) div 4);

R. Top: =tR. Top+((tR. Bottom-tR. Top) div 4);

R. Bottom: =tR. Bottom — ((tR. Bottom-tR. Top) div 4);

Canvas. Ellipse®;

End;

End;

Procedure TVisualChessBoard. DrawMove (x, y: Integer);

Const

dR=5;

Var

R: TRect;

Begin

Canvas. Pen. Color: =clLime;

Canvas. Pen. Style: =psSolid;

Canvas. Pen. Width: =1;

Canvas. Brush. Color: =clLime;

Canvas. Brush. Style: =bsDiagCross;

R: =GetCellRectByNumber (x, y);

Canvas. Ellipse (R. Left+dR, R. Top+dR, R. Right-dR, R. Bottom-dR);

End;

Procedure TVisualChessBoard. DrawStrike (x, y: Integer);

Const

dR=5;

Var

R: TRect;

Begin

Canvas. Pen. Color: =clRed;

Canvas. Pen. Style: =psSolid;

Canvas. Pen. Width: =1;

Canvas. Brush. Color: =clRed;

Canvas. Brush. Style: =bsDiagCross;

R: =GetCellRectByNumber (x, y);

Canvas. Ellipse (R. Left+dR, R. Top+dR, R. Right-dR, R. Bottom-dR);

End;

Procedure TVisualChessBoard. DrawMoves (Const Moves, Strikes: TList);

Var

CP: PCellPosition;

i: Integer;

Begin

If Moves< >nil Then

For i: =0 To Moves. Count-1 Do

Begin

CP: =Moves. Items[i];

DrawMove (CP^. X, CP^. Y);

End;

If Strikes< >nil Then

For i: =0 To Strikes. Count-1 Do

Begin

CP: =Strikes. Items[i];

DrawStrike (CP^. X, CP^. Y);

End;

End;

Procedure TVisualChessBoard. DrawMovesByNumber (x, y: Integer);

Var

Moves, Strikes, Strickens: TList;

Begin

Board. FindMoves (x, y, Moves, Strikes, Strickens);

DrawMoves (Moves, Strikes);

ClearMoves (Moves);

ClearMoves (Strikes);

ClearMoves (Strickens);

End;

end.

unit About;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, Buttons, ExtCtrls, ShellApi;

type

TfmAbout = class (TForm)

bbOk: TBitBtn;

lbTitle: TLabel;

memAbout: TMemo;

Label1: TLabel;

procedure bbOkClick (Sender: TObject);

procedure FormCreate (Sender: TObject);

private

{Private declarations}

public

{Public declarations}

end;

var

fmAbout: TfmAbout;

implementation

{$R *. DFM}

procedure TfmAbout. bbOkClick (Sender: TObject);

begin

Close;

end;

procedure TfmAbout. FormCreate (Sender: TObject);

begin

MemAbout. Text: =' ПРАВИЛА ИГРЫ. ';

MemAbout. Text: =memAbout. Text+' ';

MemAbout. Text: =memAbout. Text+'Игра идет на поле размерами 8 X 8 клеток, шашки занимают первые три ряда с каждой стороны. ';

MemAbout. Text: =memAbout. Text+'Бить можно произвольное количество шашек в любых направлениях, простая может бить назад, дамка может ходить на любое число полей, цель игры съесть или запереть все шашки противника. ';

MemAbout. Text: =memAbout. Text+'Каждая из сторон в начале игры имеет по 12 шашек. Шашки расставляются в трех ближайших к игрокам, горизонталях. ';

MemAbout. Text: =memAbout. Text+'Два центральных ряда полей остаются свободными. Здесь на этих полях, происходит сближение и первое соприкосновение противостоящих сил. ;

MemAbout. Text: =memAbout. Text+' Противники ходят поочередно, перемещая шашки своего цвета по игровым полям. Первыми начинают белые. ;

MemAbout. Text: =memAbout. Text+'Выигрывает та сторона, которой удалось уничтожить или заблокировать движение всех шашек противника. ';

end;

end.

ПоказатьСвернуть
Заполнить форму текущей работой