Как да проектираме база данни, достатъчно гъвкава, за да побере няколко много различни игри с карти.
Наскоро показахме как може да се използва база данни за съхраняване на резултати от настолни игри. Настолните игри са забавни, но не са единствената онлайн версия на класическите игри. Игрите с карти също са много популярни. Те въвеждат елемент на късмет в играта и има много повече от късмет в една добра игра на карти!
В тази статия ще се съсредоточим върху изграждането на модел на данни за съхраняване на мачове, резултати, играчи и резултати. Основното предизвикателство тук е съхраняването на данни, свързани с много различни игри с карти. Бихме могли също да обмислим анализиране на тези данни, за да определим печелившите стратегии, да подобрим собствените си игрови умения или да изградим по-добър AI противник.
Четирите игри с карти, които ще използваме в нашата база данни
Тъй като играчите не могат да контролират ръката, която им се раздава, игрите с карти съчетават стратегия, умения и късмет. Този фактор на късмет дава на начинаещия шанс да победи опитен играч и прави игрите с карти пристрастяващи. (Това се различава от игри като шах, които разчитат до голяма степен на логика и стратегия. Чувал съм от много играчи, че не се интересуват от игра на шах, защото не могат да намерят опоненти на тяхното ниво на умения.)
Ще се съсредоточим върху четири добре познати игри с карти:покер, блекджек, belot (или belote) и préférence. Всеки от тях има относително сложни правила и изисква известно време за овладяване. Съотношението на късмета и знанията също е различно за всяка игра.
Ще разгледаме набързо опростените правила и специфики за четирите игри по-долу. Описанията на игрите са доста оскъдни, но сме включили достатъчно, за да покажем различните режими на игра и разнообразните правила, които ще срещнем по време на процеса на проектиране на база данни.
Блекджек:
- Палуба: Едно до осем тестета от по 52 карти всяко; без жокер карти
- Играчи: Дилър и 1 или повече опоненти
- Използвана единица: Обикновено пари
- Основни правила: Играчите получават 2 карти, които само те могат да видят; дилърът получава две карти, едната с лицето нагоре, а другата с лицето надолу; всеки играч решава да тегли повече карти (или не); дилърът тегли последен. На картите са присвоени стойности на точки, вариращи от 1 до 11.
- Възможни действия на играча: Удари, Стой, Разцепи, Предай се
- Условие за гол и победа: Сумата от картите на играча е по-голяма от тази на дилъра; ако някой играч надхвърли 21, този играч губи.
Покер (Texas Hold’Em):
- Палуба: Стандартно (известно също като френска боя) тесте от 52 карти; без жокер карти. Картите са най-често червени и черни на цвят.
- Играчи: От две до девет; играчите се редуват да раздават
- Използвана единица:Обикновено чипове
- Основни правила: Всеки играч започва, като му се раздават две карти; играчите правят своите залози; три карти се раздават с лицето нагоре в средата на масата; играчите отново правят своите залози; четвърта карта се поставя в средата и играчите залагат отново; след това се поставя петата и последна карта и последният кръг на залагане е завършен.
- Възможни действия на играча: Фолд, кол, рейз, малък блайнд, голям блайнд, рирейз
- Цел: Комбинирайте най-добрата възможна ръка от пет карти (от двете карти в ръката на играча и петте карти в средата на масата)
- Условие за победа:Обикновено за спечелване на всички чипове на масата
Белот (хърватски вариант на Белот):
- Палуба: Обикновено традиционното немско или унгарско тесте от 32 карти; без жокер карти
- Играчи: От две до четири; обикновено четирима играчи по двойки по двама
- Използвана единица: Точки
- Основни правила: За игра с четирима играчи всеки играч получава шест карти в ръка и две карти с лицето надолу; играчите първи наддават за коз; след като козът е определен, те вземат двете обърнати надолу карти и ги поставят в ръката си; следва рунд на деклариране, по време на който се обявяват определени комбинации от карти за допълнителни точки; играта продължава, докато не бъдат използвани всички карти.
- Възможни действия на играча: Pass, Bid Suit, Declaration, Throw Card
- Гол за ръката: За да спечелите повече от половината точки
- Условие за победа: Бъдете първият отбор, спечелил 1001 точки или повече
Предпочитание:
- Палуба: Най-често традиционно немско или унгарско тесте от 32 карти; без жокер карти
- Играчи: Три
- Единици: Точки
- Основни правила: На всички играчи се раздават по 10 карти; две карти „коте“ или „талон“ се поставят в средата на масата; играчите определят дали искат да наддават за костюм; играчите решават да играят или не.
- Възможни действия на играча: Pass, Bid Suit, Play, Don’t Play, Throw Card
- Цел: Зависи от варианта на Préférence, който се играе; в стандартната версия участникът трябва да спечели общо шест трика.
- Условие за победа: Когато сборът от резултатите на тримата играчи е 0, играчът с най-малък брой точки печели.
Защо да комбинирате бази данни и игри с карти?
Нашата цел тук е да проектираме модел на база данни, който да съхранява всички необходими данни за тези четири игри с карти. Базата данни може да се използва от уеб приложение като място за съхранение на всички съответни данни. Искаме да съхраняваме първоначалните настройки на играта, участниците в играта, действията, предприети по време на игра, и резултата от една сделка, ръка или трик. Трябва също да имаме предвид факта, че един мач може да има една или повече сделки, свързани с него.
От това, което съхраняваме в нашата база данни, трябва да можем да пресъздадем всички действия, извършени по време на играта. Ще използваме текстови полета, за да опишем условията за победа, действията в играта и техните резултати. Те са специфични за всяка игра и логиката на уеб приложението ще интерпретира текста и ще го трансформира според нуждите.
Бързо въведение в модела
Този модел ни позволява да съхраняваме всички подходящи данни за играта, включително:
- Свойства на играта
- Списък с игри и мачове
- Участници
- Действия по време на игра
Тъй като игрите се различават по много начини, често ще използваме varchar(256) тип данни за описание на свойства, ходове и резултати.
Играчи, мачове и участници
Този раздел на модела се състои от три таблици и се използва за съхраняване на данни за регистрирани играчи, изиграните мачове и играчите, които са участвали.
player
таблицата съхранява данни за регистрирани играчи. username
и email
атрибутите са уникални стойности. nick_name
атрибутът съхранява екранните имена на играчите.
match
таблицата съдържа всички съответни данни за съвпадението. Обикновено мачът се състои от една или повече раздаване на карти (известни също като рундове, ръце или трикове). Всички мачове имат определени правила преди началото на играта. Атрибутите са както следва:
game_id
– препраща към таблицата, съдържаща списъка с игри (покер, блекджек, белот и преферанс в този случай).start_time
иend_time
са действителните моменти, когато мачът започва и завършва. Забележете, чеend_time
може да бъде NULL; няма да имаме стойността му, докато играта не приключи. Също така, ако съвпадението е прекратено, преди да е приключило,end_time
стойността може да остане NULL.number_of_players
– е броят на участниците, необходим за стартиране на игратаdeck_id
– препраща към тестето, използвано в играта.decks_used
– е броят на тестетата, използвани за игра на играта. Обикновено тази стойност ще бъде 1, но някои игри използват няколко тестета.unit_id
– е единицата (точки, чипове, пари и т.н.), използвана за вкарване на играта.entrance_fee
– е броят на единиците, необходими за присъединяване към играта; това може да бъде NULL, ако играта не изисква всеки играч да започне с определен брой единици.victory_conditions
– определя кой играч е спечелил мача. Ще използваме varchar тип данни, за да опише условието за победа на всяка игра (т.е. първият отбор да достигне 100 точки) и да остави приложението да го интерпретира. Тази гъвкавост оставя място за добавяне на много игри.match_result
– съхранява резултата от съвпадението в текстов формат. Както приvictory_conditions
, ще оставим приложението да интерпретира стойността. Този атрибут може да бъде NULL, защото ще попълним тази стойност в същото време, когато вмъквамеend_time
стойност.
participant
таблицата съхранява данни за всички участници в мача. match_id
и player_id
атрибутите са препратки към match
и player
маси. Заедно тези стойности образуват алтернативния ключ на таблицата.
Повечето от игрите се въртят кой играч наддава или играе първи. Обикновено в първия рунд играчът, който играе първи (първият играч) се определя от правилата на играта. В следващия рунд, играчът отляво (или понякога отдясно) от първоначалния начален играч ще отиде първи. Ще използваме initial_player_order
атрибут за съхраняване на поредния номер на играча, който отваря първия рунд. match_id
и initial_player_order
атрибутите формират друг алтернативен ключ, защото двама играчи не могат да играят едновременно.
score
атрибутът се актуализира, когато играч завърши мача. Понякога това ще бъде в един и същи момент за всички играчи (например в belot или préférence), а понякога, докато мачът все още е в ход (например покер или блекджек).
Действия и типове действия
Когато мислим за действия, които играчите могат да извършват в игра с карти, осъзнаваме, че трябва да съхраняваме:
- Какво беше действието
- Кой е извършил това действие
- Кога (при коя сделка) е извършено действието
- Кои карти са били използвани в това действие
action_type
table е прост речник, който съдържа имената на действията на играча. Някои възможни стойности включват карта за изтегляне, карта за игра, карта за пас на друг играч, чек и рейз.
В action
таблица, ние ще съхраняваме всички събития, случили се по време на сделка. deal_id
, card_id
, participant_id
и action_type_id
са препратки към таблиците, които съдържат стойности за сделка, участник в картата и action_type. Забележете, че participant_id
и card_id
могат да бъдат стойности NULL. Това се дължи на факта, че някои действия не се извършват от играчи (например дилърът тегли карта и я поставя с лицето нагоре), докато някои не включват карти (например рейз в покер). Трябва да съхраним всички тези действия, за да можем да пресъздадем целия мач.
action_order
атрибутът съхранява поредния номер на действие в играта. Например, начална оферта ще получи стойност 1; следващата оферта ще има стойност 2 и т.н. Не може да има повече от едно действие, което се случва едновременно. Следователно, deal_id
и action_order
атрибутите заедно образуват алтернативния ключ.
action_notation
атрибутът съдържа подробно описание на действие. В покера, например, можем да съхраняваме рейз действие и произволна сума. Някои действия може да са по-сложни, така че е разумно да съхраните тези стойности като текст и да оставите приложението да го интерпретира.
Сделки и поръчка за сделка
Мачът се състои от една или повече раздаване на карти. Вече обсъдихме participant
и match
таблици, но сме ги включили в изображението, за да покажем връзката им с deal
и deal_order
таблици.
deal
таблицата съхранява всички необходими данни за един екземпляр на съвпадение.
match_id
атрибутът свързва този екземпляр с подходящото съвпадение, докато start_time
и end_time
обозначава точното време, когато този екземпляр е започнал и кога е приключил.
move_time_limit
и deal_result
атрибутите са както текстови полета, използвани за съхраняване на времеви ограничения (ако е приложимо), така и описание на резултата от тази сделка.
В participant
таблицата, initial_player_order
атрибутът съхранява реда на играча за началния мач. Съхраняването на поръчките за следващи завои изисква изцяло нова таблица – deal_order
таблица.
Очевидно deal_id
и participant_id
са препратки към екземпляр на мач и участник. Заедно те образуват първия алтернативен ключ в deal_order
маса. player_order
атрибутът съдържа стойности, обозначаващи поръчките, които играчите са участвали в този мач. Заедно с deal_id
, той формира втория алтернативен ключ в тази таблица. deal_result
атрибутът е текстово поле, което описва резултата от мача за отделен играч. score
атрибутът съхранява числова стойност, свързана с резултата от сделката.
Масти, рангове и карти
Този раздел на модела описва картите, които ще използваме във всички поддържани игри. Всяка карта има боя и ранг.
suit_type
table е речник, който съдържа всички типове костюми, които ще използваме. За suit_type_name
, ще използваме стойности като „френски костюми“, „немски костюми“, „швейцарско-германски костюми“ и „латински костюми“.
suit
таблицата съдържа имената на всички костюми, съдържащи се в специфични типове колоди. Например, френската колода има костюми, наречени „Пика“, „Сърца“, „Диаманти“ и „Бафове“.
В rank
речник, ще намерим добре познати стойности на карти като „Асо“, „Кинг“, „Дама“ и „Вале“.
card
таблицата съдържа списък на всички възможни карти. Всяка карта ще се появи в тази таблица само веднъж. Това е причината suit_id
и rank_id
атрибутите формират алтернативния ключ на тази таблица. Стойностите на двата атрибута могат да бъдат NULL, тъй като някои карти нямат боя или ранг (напр. карти джокери). is_joker_card
е булева стойност, която се разбира от само себе си. card_name
атрибут описва карта чрез текст:„Асо пика“.
Карти и колода
Картите принадлежат към колодите. Тъй като една карта може да се появи в няколко тестета, ще ни трябва n:n връзка между card
и deck
таблици.
В deck
таблица, ще съхраняваме имената на всички тестета карти, които искаме да използваме. Пример за стойности, съхранени в deck_name
атрибутите са:„Стандартно тесте с 52 карти (френски)“ или „Теке с 32 карти (немски)“.
card_in_deck
Relation се използва за присвояване на карти към подходящи тестета. card_id
– deck_id
pair е алтернативният ключ на deck
маса.
Свойства на съвпадение, използвани колоди и единици
Този раздел на модела съдържа някои основни параметри за стартиране на нова игра.
Основната част на този раздел е game
маса. Тази таблица съхранява данни за игри, поддържани от приложения. game_name
атрибутът съдържа стойности като „poker“, „blackjack“, „belot“ и „préférence“.
min_number_of_players
и max_number_of_players
са минималният и максималният брой участници в мача. Тези атрибути служат като граници на играта и се показват на екрана в началото на мача. Човекът, който инициира съвпадението, трябва да избере стойност от този диапазон.
min_entrance_fee
и max_entrance_fee
атрибути обозначава диапазона на входната такса. Отново, това се основава на играта, която се играе.
В possible_victory_condition
, ще съхраняваме всички условия за победа, които могат да бъдат приписани на мач. Стойностите са разделени с разделител.
unit
речникът се използва за съхраняване на всяка единица, използвана във всички наши игри. unit_name
атрибутът съдържа стойности като „точка“, „долар“, „евро“ и „чип“.
game_deck
и game_unit
таблиците използват същата логика. Те съдържат списъци с всички колоди и единици, които могат да се използват в мач. Следователно, game_id
– deck_id
двойка и game_id
– unit_id
двойки образуват алтернативни ключове в съответните им таблици.
Резултати
В нашето приложение ще искаме да съхраняваме резултатите за всички играчи, които са участвали в нашите игри с карти. За всяка игра се изчислява и съхранява една цифрова стойност. (Изчислението се основава на резултатите на играча във всички игри от един тип.) Този резултат на играча е подобен на ранг; позволява на потребителите да знаят приблизително колко добър е играчът.
Обратно към процеса на изчисление. Ще създадем n:n връзка между player
и game
маси. Това е player_score
таблица в нашия модел. player_id
и score_id
” заедно образуват алтернативния ключ на таблицата. „score
атрибутът се използва за съхраняване на споменатата по-рано числова стойност.
Има различни игри с карти, които използват много различни правила, карти и тестета. За да създадем база данни, която съхранява данни за повече от една игра с карти, трябва да направим някои обобщения. Един от начините да направим това е като използваме описателни текстови полета и оставяме приложението да ги интерперира. Бихме могли да измислим начини да покрием най-често срещаните ситуации, но това би усложнило експоненциално дизайна на базата данни.
Както показа тази статия, можете да използвате една база данни за много игри. Защо бихте направили това? Три причини:1) можете да използвате повторно същата база данни; 2) това би опростило анализите; и това би довело до 3) изграждането на по-добри AI опоненти.