Пишем игру 21 (очко). Пример использования 1С и ActiveX

Программирование - Практика программирования

Пишем игру "очко". Программный код состоит из двух частей: 1.Разработка компоненты работы с графикой на Delphi 2.Логическая реализация игрового процесса на 1С

Часть 1. Пишем компоненту работы с графикой на Delphi.

Для работы с графикой решено использовать внешнюю компоненту, т.к возможности 1С в этом ограничены.

Открываем старый и добрый Дельфи 7, и создаем новый проект.

Переходим на вкладку ActiveX и выбираем ActiveX Form

Вводим имя ActiveX

Для хранения изображений колоды карт будем использовать компонент TImage. Кидаем его на форму.

и загружаем в TImage колоду карт найденную в интернете:

Отображать карты будем на канве формы, предварительно вырезав нужную из TImage. Поэтому, чтобы TImage не светилось - прячем его. Для этого свойство Visible у TImage ставим в False.

Теперь опишем методы работы с изображением. Их будет два:

1. Отображение на канве формы нужной игральной карты.

2. Очистка самой канвы.

Открываем Type Library

И добавляем 2 метода: AddCard и ClearCanvas

у метода AddCard вводим параметры: x,y - это координаты отображения карты. row, column - это номер игральной карты из загруженного нами изображения. Например 0 ряд 2 карта.

Опишем реализацию методов в модуле:

//вырезаем и отображаем на канве нужную нам игральную карту
procedure Tw0rm21.AddCard(x, y, row, colimn: Integer);
const
  h=215; //высота карты
  w=148; //длина карты
begin
  Canvas.CopyRect(Rect(x, y, x+w, y+h), Image1.Canvas, Rect(colimn*w ,row*h, (colimn*w)+w, (row*h)+h));
end;

//Очищаем канву
procedure Tw0rm21.ClearCanvas;
begin
  InvalidateRect(0, nil, true);
end;

На этом реализация компоненты закончена. Вы можете ее в дальнейшем доработать как вам нужно.

Осталось только зарегистировать компоненту.

2. Реализуем игровую логику на 1С

Создаем внешнюю обработку. На форму обработки размещаем наш компонент:

Располагаем контролы (кнопки, надписи) на форме:

И описываем реализацию логики игры:

Перем КолодаКарт, ОбщийСчетИгрок, ОбщийСчетПК;
Перем ИгрокX, ИгрокY,  КомпX, КомпY, ШагКарты;
Перем карта1ПК,	карта2ПК;
Перем СтоимостьКарт;


Процедура КнопкаВыполнитьНажатие(Кнопка)
	Колонка = ПолучитьСлучайноеЧисло(0, 12);
	Ряд = ПолучитьСлучайноеЧисло(0, 3);
	ЭлементыФормы.ЭлементУправления1.AddCard(1,1, Ряд, Колонка);
КонецПроцедуры


Функция ПолучитьСлучайноеЧисло(Мин,Макс) 

	//вместо Randomize 
	Для н = 1 По 100 Цикл 
		Уник = Новый УникальныйИдентификатор; 
	КонецЦикла; 

	//генерируем GUID 
	Уник = СокрЛП(Новый УникальныйИдентификатор); 

	//оставляем только цифры 
	Уник = СтрЗаменить(Уник,"-",""); 
	Уник = СтрЗаменить(Уник,"a",""); 
	Уник = СтрЗаменить(Уник,"b",""); 
	Уник = СтрЗаменить(Уник,"c",""); 
	Уник = СтрЗаменить(Уник,"d",""); 
	Уник = СтрЗаменить(Уник,"e",""); 
	Уник = СтрЗаменить(Уник,"f",""); 

//знаменатель должен иметь такое же количество нулей + 1 
	Знаменатель = 10; 
	Для н = 2 По (СтрДлина(СтрЗаменить(Уник,Символы.НПП,""))) Цикл 
		Знаменатель = Знаменатель * 10; 
	КонецЦикла; 

	Случ = Число(Уник) / Знаменатель; //здесь получается дробное случайное число от 0 до 1 

	//преобразуем его в случайное число из заданного интервала, округляем до целого 
	ЧислоИзИнтервала = Мин(Макс(Окр(Мин + (Макс-Мин)*Случ),Мин),Макс); 

	Возврат ЧислоИзИнтервала; 

КонецФункции

Процедура РаздатьКартыНажатие(Элемент)
	СтоимостьКарт = "11;2;3;4;5;6;7;8;9;10;2;3;4";
	СтоимостьКарт = СтрЗаменить(СтоимостьКарт, ";", Символы.ПС);
	ЭлементыФормы.ЭлементУправления1.ClearCanvas();
	ИнициализоватьКолоду();
	СчетИгрок = 0;
	СчетПК	= 0;
	ИгрокX = 2; ИгрокY = 3;
	КомпX = 2;  КомпY = 500;
	Шагкарты = 180;
	карта1ПК = Неопределено;
	карта2ПК = Неопределено;	
	
	карта1Игрок =ПолучитьКарту();
	карта2Игрок =ПолучитьКарту();
	СчетИгрок = СчетИгрок + карта1Игрок.Стоимость;
	СчетИгрок = СчетИгрок + карта2Игрок.Стоимость;
	ЭлементыФормы.ЭлементУправления1.AddCard(ИгрокX, ИгрокY, карта1Игрок.Ряд, карта1Игрок.Колонка);
	ИгрокX = ИгрокX + Шагкарты;
	ЭлементыФормы.ЭлементУправления1.AddCard(ИгрокX, ИгрокY, карта2Игрок.Ряд, карта2Игрок.Колонка);
	
	карта1ПК =ПолучитьКарту();
	карта2ПК =ПолучитьКарту();
	ЭлементыФормы.ЭлементУправления1.AddCard(КомпX, КомпY, 4, 2);
	КомпX = КомпX + Шагкарты;
	ЭлементыФормы.ЭлементУправления1.AddCard(КомпX, КомпY, 4, 2);
	
	КомпX = 2;
	
	ОбновитьФорму(Ложь, Истина, Истина);
	
	Если СчетИгрок = 21 Тогда 
		Предупреждение("У ВАС ОЧКО! Вы победили!");
		ОбщийИгрок = ОбщийИгрок + 1;
		ОбновитьФорму(Истина, Ложь, Ложь);
		Возврат;
	КонецЕсли;	

	Если СчетИгрок = 22 Тогда 
		Предупреждение("У ВАС ЗОЛОТОЙ! Вы победили!");
		ОбщийИгрок = ОбщийИгрок + 1;
		ОбновитьФорму(Истина, Ложь, Ложь);
		Возврат;
	КонецЕсли;

	
КонецПроцедуры

Процедура ИнициализоватьКолоду()
	Колонка = 13;
	Строк  = 4;
	КолодаКарт = Новый Массив(Строк, Колонка);
	Для а = 0 по Строк-1 Цикл 
		Для б = 0 по Колонка-1 Цикл 
			КолодаКарт[а][б] = 0;
		КонецЦикла;	
	КонецЦикла;	
КонецПроцедуры	

Функция ПолучитьКарту()
	Карта = Новый Структура;
	КартаНайдена = Ложь;
	
	
	Пока КартаНайдена = Ложь Цикл 
		
		Колонка = ПолучитьСлучайноеЧисло(0, 12);
		Ряд = ПолучитьСлучайноеЧисло(0, 3);	

		Если КолодаКарт[Ряд][Колонка] = 0 Тогда 
			Карта.Вставить("Ряд", Ряд);
			Карта.Вставить("Колонка", Колонка);
			Стоимость = СтрПолучитьСтроку(СтоимостьКарт, Колонка+1);
			Карта.Вставить("Стоимость", Число(Стоимость));
			//Сообщить(Стоимость);
			КолодаКарт[Ряд][Колонка] = 1;
			КартаНайдена = Истина;
		КонецЕсли;	
			
	КонецЦикла;	
	
	Возврат(Карта);
КонецФункции	

Процедура ВзятьКартуНажатие(Элемент)
	ИгрокX = ИгрокX + Шагкарты;
	карта = ПолучитьКарту();
	ЭлементыФормы.ЭлементУправления1.AddCard(ИгрокX, ИгрокY, карта.Ряд, карта.Колонка);
	СчетИгрок = СчетИгрок + карта.Стоимость;
	Если СчетИгрок > 21 Тогда 
		Предупреждение("У ВАС ПЕРЕБОР! Вы проиграли!");
		ОбщийПК = ОбщийПК + 1;
		ОбновитьФорму(Истина, Ложь, Ложь);
	КонецЕсли;	
КонецПроцедуры

Процедура ХватитНажатие(Элемент)
	ОбновитьФорму(Ложь, Ложь, Ложь);	
	Пауза(2);
	ЭлементыФормы.ЭлементУправления1.AddCard(КомпX, КомпY, карта1ПК.Ряд, карта1ПК.Колонка);
	СчетПК = СчетПК + карта1ПК.Стоимость;
	Пауза(2);
	КомпX = КомпX + Шагкарты;
	ЭлементыФормы.ЭлементУправления1.AddCard(КомпX, КомпY, карта2ПК.Ряд, карта2ПК.Колонка);
	СчетПК = СчетПК + карта2ПК.Стоимость;
	
	Если СчетПК = 22 Тогда 
		Предупреждение("У Меня ЗОЛОТОЙ! Я Победил!");
		ОбщийИгрок = ОбщийИгрок + 1;
		ОбновитьФорму(Истина, Ложь, Ложь);
		Возврат;
	КонецЕсли;

	
	Пока СчетПК < 17 Цикл  
		Пауза(2);
		КомпX = КомпX + Шагкарты;
		картаПК =ПолучитьКарту();
		ЭлементыФормы.ЭлементУправления1.AddCard(КомпX, КомпY, картаПК.Ряд, картаПК.Колонка);
		СчетПК = СчетПК + картаПК.Стоимость;
	КонецЦикла;
	
	ОбновитьФорму(Истина, Ложь, Ложь);
	
	Если СчетПК > 21 Тогда 
		Предупреждение("У меня перебор! ВЫ ВЫЙГРАЛИ!");
		ОбщийИгрок = ОбщийИгрок + 1;
		Возврат;
	КонецЕсли;	
	
	Если СчетПК = 21 Тогда 
		Предупреждение("У меня ОЧКО! Я победил!");
		ОбщийПК = ОбщийПК + 1;
		Возврат;
	КонецЕсли;
	
	Если СчетПК = СчетИгрок Тогда 
		Предупреждение("Ничья!");
		Возврат;
	КонецЕсли;	
	
	Если СчетПК > СчетИгрок Тогда 
		Предупреждение("Я победил!"); 
		ОбщийПК = ОбщийПК + 1;
		Возврат;
	КонецЕсли;	
	
	Если СчетПК < СчетИгрок Тогда 
		Предупреждение("Вы Победили!");
		ОбщийИгрок = ОбщийИгрок + 1;
		Возврат;
	КонецЕсли;	


	
КонецПроцедуры

Процедура Пауза(Секунд)
	
  КонДата = ТекущаяДата() + Секунд;
  Пока ТекущаяДата() < КонДата Цикл
      // ждемссс....
  КонецЦикла;	
  
КонецПроцедуры	

Процедура ПриОткрытии()
	ОбщийИгрок = 0;
	ОбщийПК = 0;
	ОбновитьФорму(Истина, Ложь, Ложь);
КонецПроцедуры

Процедура ОбновитьФорму(РаздатьКарты, ВзятьКарту, Хватит)
	ЭлементыФормы.РаздатьКарты.Доступность = РаздатьКарты;
	ЭлементыФормы.ВзятьКарту.Доступность   = ВзятьКарту;
	ЭлементыФормы.Хватит.Доступность       = Хватит;
КонецПроцедуры

Вызов наших методов из компоненты написанной на Delphi выглядит так: ЭлементыФормы.ЭлементУправления1.AddCard(1,1, Ряд, Колонка); и ЭлементыФормы.ЭлементУправления1.ClearCanvas();

запускаем и проверяем

На этом у меня все! Вы можете сами доработать правила игрового процесса, так как считаете нужным.

Архив с примером выкладываю ниже.

Перед тем как запустить обработку из архива необходимо зарегистрировать компоненту w0rm21Proj1.ocx. 

Готов ответить на вопросы.

Спасибо за внимание!

 

Скачать файлы

Наименование Файл Версия Размер
Пишем игру 21 (очко). Пример использования 1С и ActiveX (Программный код 1С и Delphi):
.zip 1,49Mb
23.05.17
3
.zip 1,49Mb 3 Скачать

См. также

Комментарии
1. Дмитрий Котов (rpgshnik) 90 23.05.17 10:29 Сейчас в теме
Это круто!
Прикрепленные файлы:
DrAku1a; Tolpinski; Romakon92; CyberCerber; TDAlleaMebeli; Smirnov1980; +6 Ответить
2. Random Name (BackinSoda) 23.05.17 10:39 Сейчас в теме
Что это за правило такое - "Золотой" ?
CyberCerber; Smirnov1980; +2 Ответить
3. Дмитрий Васильев (user621724_Dimav1979) 202 23.05.17 10:52 Сейчас в теме
(2) в данном случае это когда два туза сразу выпало...
Smirnov1980; +1 Ответить
7. Константин Гейнрих (CyberCerber) 159 23.05.17 13:01 Сейчас в теме
(3) Не заметил в коде, а где-то проверяется, что это именно два туза, а не, например 10 + 10 + 2?
8. Дмитрий Васильев (user621724_Dimav1979) 202 23.05.17 17:48 Сейчас в теме
(7) Проверка на Золотой сразу после раздачи первых двух карт, если счет будет 22 - то это и есть 2 туза
4. 1ckun (smirnov.es) 23.05.17 11:17 Сейчас в теме
Респект таким парням. Ждем реализации на мобильной платформе
CyberCerber; Smirnov1980; madonov; rpgshnik; +4 Ответить
5. Александр Смирнов (Smirnov1980) 23.05.17 12:07 Сейчас в теме
Это cool !

ps В базовую поставку 1С уже давно требуется пасьянс "косынка" )))).
AlexGroovy; rpgshnik; +2 Ответить
6. Константин Гейнрих (CyberCerber) 159 23.05.17 13:00 Сейчас в теме
Круто! Лет 10 назад делал тоже 21 на Делфи 7... Интересно, можно будет его теперь в 1С встроить :-)
9. Евгений Стоянов (quick) 559 23.05.17 22:22 Сейчас в теме
А слабо на делфях в 7-ку сделать форму, да что бы калбэки уходили в 1С? ;)
Если делфи еще интересно, могу посоветовать глянуть https://github.com/WorldException/v7SimpleAddIn
там уже XE2, но сам принцип использовать RTTI для внешних компонент может быть интересен.
rpgshnik; +1 Ответить
10. Дмитрий Васильев (user621724_Dimav1979) 202 24.05.17 04:41 Сейчас в теме
(9) Если кому интересно, могу статью опубликовать отлавливание событий из формы ActiveX в 1С...можно на примере игры морской бой или на примере ведения карты склада ячеистого газобетона
smirnov.es; user657734_YGREEN; Rain88; Trancer64; monsterZE; inf012; +6 Ответить
12. Дмитрий Котов (rpgshnik) 90 24.05.17 17:45 Сейчас в теме
(10) Нужно бооооольше уроков
Прикрепленные файлы:
11. Дмитрий Васильев (user621724_Dimav1979) 202 24.05.17 08:11 Сейчас в теме
Выкладываю обработку с компонентой
Прикрепленные файлы:
21_example.zip
monsterZE; +1 Ответить
13. PerlAmutor IC (PerlAmutor) 7 24.05.17 21:35 Сейчас в теме
Немного полезной критики =) Delphi? Гхм, пора бы ей уже на покой. Компонента не переносима на другие платформы, про веб-клиент, можно забыть. В безопасном режиме работать не будет. Тут возникает главный вопрос, зачем было связывать это вообще с 1С? Хотите того же самого - используйте HTML5 и JavaScript. К сожалению тоже будет не переносимо, т.к. Internet Explorer нет в Linux. Легких путей, к сожалению, разработчики 1С нам не предлагают. Управляемые Формы, опять же к сожалению, оказались менее управляемые чем Обычные. Надеюсь они когда нибудь встроят в свою платформу браузер Chromium и позволят управлять всем исходным кодом страницы при использовании веб-клиента.

Старайтесь вот так не писать (красиво, но не практично), я обжегся уже на этом
Перем КолодаКарт, ОбщийСчетИгрок, ОбщийСчетПК;


1С настолько тупа, что по Ctrl+Space не находит переменных ОбщийСчетИгрок и ОбщийСчетПК объявленных через запятую.

Только так будет находить:

Перем КолодаКарт;
Перем ОбщийСчетИгрок;
Перем ОбщийСчетПК;
14. Андрей Акулов (DrAku1a) 1216 25.05.17 02:34 Сейчас в теме
(13) Delphi - это классика.
HTML5 не обязателен, обычного HTML + JS достаточно будет.
16. Александр Орефков (orefkov) 1472 27.05.17 15:25 Сейчас в теме
(13)
1С настолько тупа, что по Ctrl+Space не находит переменных ОбщийСчетИгрок и ОбщийСчетПК объявленных через запятую.

Ну, может человек снегопат использует и знать не знает ни о ctrl+space, ни о таких тонкостях его использования :)
rpgshnik; +1 Ответить
15. Александр Орефков (orefkov) 1472 27.05.17 13:54 Сейчас в теме
Получение следующей карты сделано конечно плоховато. На такой игре, как очко, конечно сойдёт, так как много карт всё-равно не выходит, но для игр с длинной раздачей - для последних карт может кучу раз цикл вхолостую крутить, пока попадёт в еще не выданные карты. Обычно делают нормальное тасование колоды. Для 1С например, можно сделать ТЗ с колонками ЗначениеКарты и КлючСортировки. Заполнить КлючСортировки случайными числами и отсортировать по ней. Потом брать последовательно карты из отсортированной ТЗ.
rpgshnik; +1 Ответить
17. Дмитрий Васильев (user621724_Dimav1979) 202 27.05.17 16:02 Сейчас в теме
(15) Александр полностью с вами согласен. Алгоритм можно оптимизировать до бесконечности...но главная цель данной статьи не в алгоритме игры...а в навыках применения компоненты актив икс
asmuk; Fox-trot; +2 Ответить
Оставьте свое сообщение