Техника программирования: как создать простую карточную игру

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

итак, проблемы программирования мне интересно о следующем:

  1. Как я могу сортировать/рандомизировать колоду карт? Существует четыре типа, каждый из которых имеет 13 значений. В конце концов, могут быть только уникальные значения, поэтому выбор случайных значений может генерировать дубликаты.

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

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

кроме того, указатели на сайты, где объясняются такие проблемы, были бы отличным ресурсом!

Спасибо за ваши комментарии, ответы и отзывы!

6 ответов


С чего начать

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

на Array#shuffle метод основан на алгоритме перемешивания кнута-Фишера-Йейтса. http://en.wikipedia.org/wiki/Fisher-Yates_shuffle

class Card
  RANKS = %w(2 3 4 5 6 7 8 9 10 J Q K A)
  SUITS = %w(Spade Heart Club Diamond)

  attr_accessor :rank, :suit

  def initialize(id)
    self.rank = RANKS[id % 13]
    self.suit = SUITS[id % 4]
  end
end

class Deck
  attr_accessor :cards
  def initialize
    # shuffle array and init each Card
    self.cards = (0..51).to_a.shuffle.collect { |id| Card.new(id) }
  end
end

# people with Ruby 1.9 (or 1.8.7 with backports) can safely ignore this duck punch
class Array
  # knuth-fisher-yates shuffle algorithm
  def shuffle!
    n = length
    for i in 0...n
      r = rand(n-i)+i
      self[r], self[i] = self[i], self[r]
    end
    self
  end
  def shuffle
    dup.shuffle!
  end
end

тест

d = Deck.new
d.cards.each do |card|
  puts "#{card.rank} #{card.suit}"
end

выход

6 Spade
5 Heart
2 Heart
8 Heart
8 Diamond
7 Club
J Diamond
4 Club
K Spade
5 Diamond
J Heart
8 Spade
10 Club
4 Diamond
9 Heart
7 Diamond
3 Diamond
K Diamond
7 Spade
Q Diamond
9 Diamond
6 Heart
A Heart
9 Club
A Spade
5 Club
J Club
Q Spade
2 Club
2 Spade
Q Heart
A Diamond
10 Spade
10 Diamond
Q Club
3 Club
A Club
K Club
6 Club
10 Heart
2 Diamond
3 Spade
K Heart
5 Spade
9 Spade
7 Heart
4 Spade
J Spade
3 Heart
4 Heart
8 Club
6 Diamond

вместо того, чтобы зубрить все это в комментарии, я добавляю это как примечание для людей, которые могут найти его полезным. Родной Руби 1.9 это Array#shuffle! и Array#shuffle фактически использует алгоритм перемешивания кнута-Фишера-Йейтса.

ruby-1.9.1-p376/array.c

/*
 *  call-seq:
 *     array.shuffle!        -> array
 *  
 *  Shuffles elements in _self_ in place.
 */

static VALUE
rb_ary_shuffle_bang(VALUE ary)
{
    long i = RARRAY_LEN(ary);

    rb_ary_modify(ary);
    while (i) {
    long j = rb_genrand_real()*i;
    VALUE tmp = RARRAY_PTR(ary)[--i];
    RARRAY_PTR(ary)[i] = RARRAY_PTR(ary)[j];
    RARRAY_PTR(ary)[j] = tmp;
    }
    return ary;
}


/*
 *  call-seq:
 *     array.shuffle -> an_array
 *  
 *  Returns a new array with elements of this array shuffled.
 *     
 *     a = [ 1, 2, 3 ]           #=> [1, 2, 3]
 *     a.shuffle                 #=> [2, 3, 1]
 */

static VALUE
rb_ary_shuffle(VALUE ary)
{
    ary = rb_ary_dup(ary);
    rb_ary_shuffle_bang(ary);
    return ary;
}

Не трудитесь искать пакет AI

вы узнаете больше и получите большее удовлетворение, кодируя "AI" самостоятельно.

начать просто, просто подумайте:

  • игры - какие карты были воспроизведены или видели, какие карты видны всем игрокам
  • стратегия - как реагирует компьютерный игрок на основе его текущей руки и его знания состояния игры

раз у вас есть, что работает, вы можете получить разработать более сложные стратегии:

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

тогда, если вы хотите сделать действительно сложные, Вы можете начать смотреть в оппонента моделирование


ответ Мачека хорош, насколько для настройки колоды.

вы также спросили о других сущностях.

вы, вероятно, хотите четырех "игроков". Каждый игрок может быть человеком или машиной.

для реализации человека игрока, вы интерфейс с экрана / мыши / клавиатуры; для реализации машины контролируемых игроков, у вас есть рука, и вы можете увидеть некоторые центральные карты на столе (все игроки должны знать о центральной таблице, которая содержит любые карты это было бы на столе).

оттуда логика основана на том, в какую игру вы играете.

Как только ваш "игрок" (AI) получает ход (например, a takeTurn метод вызывается на ваш объект игрока), он должен изучить свои карты и принять правильные решения-принимая карты из стопок на столе или размещения карт из рук на стол. (Стол почти наверняка имеет по крайней мере две стопки, к которым игрок может получить доступ - "рисовать" и "отбрасывать".)

когда человек игрок имеет takeTurn вызванный метод, он должен взаимодействовать с экраном -- обновление руки игрока, позволяя ему рисовать и отбрасывать.

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

...Все еще думаю... Я могу добавить больше...


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

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

где у вас нет всей информации (т. е. вы не можете видеть свои карты противников), вы имитируете ее. Под имитацией я подразумеваю догадку. Среднее значение всех симуляций / догадок даст вам хорошее представление о том, какие ветви деревьев "вероятно, будут лучшими".

Если вы можете сделать все, что вы хорошо на пути (и это действительно хорошее упражнение), есть сотни статей об ИИ, google будет вашим другом. Единственная проблема с подход, который я описал, может быть отчаянно медленным, но есть много умных методов, чтобы ускорить его, как таблицы транспозиции, альфа-бета-обрезка и т. д... что я не советую вам делать прямо сейчас.


что-то очень простое, чтобы вы начали:

class CardGame
  DECK = %w[A 2 3 4 5 6 7 8 9 T J Q K].product(%w[c d h s]).map(&:join)

  def initialize(decks=1)
    @decks = decks
  end

  def shuffle
    @playing_deck = (DECK*@decks).shuffle
  end

  def deal(players=1, cards=5)
    shuffle
    @dealt = Array.new(players) { Array.new }

    @dealt.map { |hand| cards.times { hand << @playing_deck.pop } }
  end

  def display
    @dealt.each_with_index { |cards, i| puts "Player #{i+1}: #{cards.join(' | ')}" }
    puts "Cards used: #{@dealt.flatten.size}"
    puts "Cards remaining: #{@playing_deck.size}"
  end

  private :shuffle   
end

game1 = CardGame.new   
game1.deal   
game1.display    
puts 
game1.deal(4)  
game1.display   
puts   
game2 = CardGame.new(2)   
game2.deal(6,10)   
game2.display