I've been reading through the Squib docs and messing with my own example but haven't yet found a syntax for having an arbitrary number of symbols the way that Dungeon Mayhem has in the upper left of its cards.
I want to be able to specify that a card can have multiples of each icon and have them "stack" on the left side of the card.
For now, I'm assuming the icons are always in a specific order (all specials, all defends, all attacks, all draws, all heals, all plays) but it might be nice to eventually supply the order as well (in which case I'd probably use some string like "daah" for Defend, Attack, Attack, Heal)
Some examples:
4 attacks
3 defense
2 draws
2 extra plays
2 draws, 1 heal
1 special power
1 special power, 1 heal
1 special power, 1 extra play
Here are the cards that show these examples:
Yep! You can do this a couple of different ways. Here's one way from this sample: https://github.com/andymeneely/squib/blob/dev/samples/ranges/_ranges.rb#L52-L60.
# Useful idiom: draw a different number of images for different cards
# hearts = [nil, 1, 2] # i.e. card 0 has no hearts, card 2 has 2 hearts drawn
1.upto(2).each do |n|
range = hearts.each_index.select { |i| hearts[i] == n}
n.times do |i|
svg file: 'glass-heart.svg', range: range,
x: 150, y: 55 + i * 42, width: 40, height: 40
end
end
Related
Trying to wrap my head around this but not able to come up with the algorithm. Maybe someone can help? The question is as follows:
You’re writing part of the code for an online card game. In the game, players pass cards right and left around a table. In your online version, you want to allow users to set up a game with some number of human players and some number of “bot” (automated) players. In order to be fair, you want to spread out the bots as evenly as possible around the table; for example, if there are 6 total players and 3 are bots, every third seat should be a bot, and the rest should be humans.
Example
Input: "6 3"
Output "HHBHHBHHB"
Basically given the input of "6 3" (where first number is number of humans and second number is number of bots) I need to return the seating arrangement that spaces out the bots as evenly as possible. "H" represents Human and "B" represents bots.
I can't think of the logic to implement this. I get a feeling it's embarrassingly simple but I'm drawing a blank. Can someone help please? Doesn't matter what coding language you use. I'm more interested in the algorithm and solution.
EDIT: Found out how to do it when Humans and Bots are nice numbers that divide into each other (Humans divided by bots: 9 and 3, 6 and 2, 10 and 5, etc.) but how about for any combination?
Example: Input "6 4"
You would think that the following will work:
HHBHHBHBHB
but that is not as evenly as possible. The following is the better solution:
HHBHBHHBHB
Here is a generic approach for any number of different classes of participants. I decided to offer an example with (E)xperts, (H)umans and (B)ots. It tries to be even no matter what.
It isn't perfectly optimal in the general case, but it is optimal in the simple case.
import random
def distribute (count_dict):
total = sum(count_dict.values())
freq = {}
state = {}
for key, count in count_dict.items():
freq[key] = count/total
state[key] = random.random() # Start randomly
answer = []
for i in range(total):
best_state = -1
best_key = None
for key, cur_state in state.items():
cur_state += freq[key]
state[key] = cur_state
if best_state < cur_state:
best_state = cur_state
best_key = key
answer.append(best_key)
state[best_key] -= 1
return answer
print(distribute({"E": 3, "B": 7, "H": 11}))
I'm looking for a way to 'project' a series of 'ranges' into a series of values. Applications would be histograms with uneven bins or creation of lookup tables.
An example:
0 to 14 => 0
15 to 234 => 1
235 => 2
236 to 255 => 3
The actual order of 'result' values (0, 1, 2, 3) on the right don't really matter as long as they are between 0 and 3, so that I can use a small lookup table after. It would be even better if I could make this work on floating point values (on the left).
I know I could use an 8-bit lookup table here and repeat values but I'd like to find a way to 'perfect hash' this: through a series of systematic operations (as small as possible, no branches) compute the right from the left values, to have the smallest possible result space.
I can't seem to find the correct series of magic Google incantations for this kind of algorithm.
The computing duration of this 'hash' or 'projection' function can be in the days if necessary.
If you have n ranges without overlap or gaps you can generate some simple code using O(log2(n)) instructions to to this lookup. I will demonstrate with some Python code.
# Must be a power of two, extend with zeros on the right if needed.
lookup = [0, 15, 235, 236]
def index(x):
i = 0
# ceil(log2(len(lookup))) iterations in the following pattern.
# We only need 2 iterations here.
# ...
# i += (x >= lookup[i+8]) << 4
# i += (x >= lookup[i+4]) << 3
i += (x >= lookup[i+2]) << 2
i += (x >= lookup[i+1]) << 1
return i
This is known as a branchless binary search. E.g. even if you have 216 ranges, this would only require 32 additions and 16 table lookups, comparisons, and bitshifts to compute the exact index.
I have a set of integers, each with a specific range:
foo = [1, 5]
bar = [1, 10]
baz = [1, 200]
I can calculate how many bits are required to store each number separately based on the number of different states that they can have:
foo = 5 possible states ~ 3 bits
bar = 10 possible states ~ 4 bits
baz = 200 possible states ~ 8 bits
Which gives me a total of 15 bits. But every number has a range that is unused, resulting in wasted space. I can instead calculate the required bits for the whole set by calculating all the possible states of all the numbers combined:
5 * 10 * 200 = 10000 possible states ~ 14 bits
This could save me a whole bit!
And this is where my question comes in: what is the best way to load and store numbers using this type of layout?
A list of variables with different ranges like this:
foo = [1, 5]
bar = [1, 10]
baz = [1, 200]
Can (almost?) be interpreted as a mixed-radix number representation. If they started at zero the correspondence would be immediate, but since these start at one (or in general: if they are any finite set of possibilities) they must be remapped a little first, here just by subtracting one for conversion to the "packed" state and adding one back when decoding it again.
The encoding is nice and easy, involving only cheap operations:
packed = (foo - 1) + 5 * (bar - 1) + (5 * 10) * (baz - 1)
The scale factors come from the number of possible states of course. Every element needs to be remapped into a contiguous range starting at zero, and then scaled by the product of the #states of the preceding elements, with the first being scaled by 1 (the empty product). By the way note that [1 .. 5] has 5 states, not 4.
Decoding involves remainders and divisions, the simplest (but not in general the fastest) way is extracting digit-by-digit:
// extract foo
foo = packed % 5 + 1
// drop foo from packed representation
packed /= 5
// extract bar (which is now the lowest digit in 'packed')
bar = packed % 10 + 1
// drop bar
packed /= 10
// top digit is left over
baz = packed + 1
For larger examples it would be more efficient to first "chop" the packed number into a few separate parts, and then decode those independently. This prevents having a long chain of dependent operations, which the digit-by-digit method naturally results in.
Working directly with the packed representation is generally tricky, except to add and subtract from the elements if you know that would not overflow.
The problem sounds like this, we are given n-texts and they are going to be placed on a p number of tapes/bands(don't really know what's the equivalent in english, but I think you understand what I'm talking about).
In order to read the text situated at a position k on one of the bands, we have to read the texts from positions 1,2,...,k on the certain band. Each text has its' own length.
Now, we have to figure out a way of placing the texts on the p-bands so that we get a global accesing time that is minimum. The global accesing time is calculated by adding all the total accesing times from each band.
The formula for calculating the total accesing time of a band is:
n_
\ [L(T1)+L(T2)+...+L(Ti)]
/_
i=1
Now, that little drawing I did is SUM from 1 to n;
L(T i) is the length of T i;
T i is the text situated at position i on the respective band;
Here is an equivalent in "pseudocode" in case it helps:
n-number of texts;
Band[n]-array of texts
sum=0, sum2=0;
for(int i=0;i<n;i++)
{sum=0;
for(int j=0;j<=i;j++ )
sum=sum+Band[j].length;
sum2=sum2+sum; }
return sum2;
Here's an example to clarify the problem:
say p is 3, so we get 3 bands
say n is 9, so we get 9 texts and the lengths are : 2, 3, 4, 5, 6, 7, 8, 9, 10
and they are placed on the bands in the following way:
band-1: 2, 5, 8 -> total accesing time of band-1: 24
band-2: 3, 6, 9 -> total accesing time of band-2: 30
band-3: 4, 7, 10 -> total accesing time of band-3: 36
the global accesing time: 24 + 30 + 36 = 90
I'll refer to text position as the number of texts that appear after a specific text in a tape, it also represents how many additional times will the text be read.
Since you are solely interested in the sum of access time there's no real meaning to how are the texts grouped into tapes but what is the position of each text, switching 2 texts in the same position but on different tapes for example won't change the global access time.
Switching 2 texts of different size on different positions will change the time though, generally longer texts should be placed in lower positions (closer to the end)
The algorithm can be greedy, go over the texts from the longest to the shortest and place each text in the last available spot on one of the tapes with the least texts in it, so if for example there are 10 texts and 5 tapes then the longer 5 texts will be in the end of each tape and the shorter 5 texts will be in the beginning of it.
as I am learning the Ruby language, I am getting closer to actual programming. I was thinking of creating a simple card game. My question isn't Ruby oriented, but I do know want to learn how to solve this problem with a genuine OOP approach. In my card game, I want to have four players, using a standard deck with 52 cards, no jokers/wildcards. In the game, I won't use the ace as a dual card, it is always the highest card.
So, the programming problems I wonder about are the following:
How can I sort/randomize the deck of cards? There are four types, each having 13 values. Eventually there can be only unique values, so picking random values could generate duplicates.
How can I implement a simple AI? As there are tons of card games, someone would have figured this part out already, so references would be great.
I am a true Ruby nuby, and my goal here is to learn to solve problems, so pseudo code would be great, just to understand how to solve the problem programmatically. I apologize for my grammar and writing style if it's unclear, for it is not my native language.
Also, pointers to sites where such challenges are explained would be a great resource!
Thank you for your comments, answers and feedback!
Something to get you started
You can ensure unique cards very easily by using numbers from 0 to 51.
The Array#shuffle method is based off the Knuth-Fisher-Yates shuffle algorithm. 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
test
d = Deck.new
d.cards.each do |card|
puts "#{card.rank} #{card.suit}"
end
output
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
Rather than cramming this all in a comment, I'm adding this as a note for people that might find it useful. Ruby 1.9's native Array#shuffle! and Array#shuffle does in fact use the Knuth-Fisher-Yates shuffle algorithm.
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;
}
Don't bother looking for an AI package
You will learn more and get greater satisfaction by coding the "AI" yourself.
Start simple, just consider:
game state - what cards have been played or seen, what cards are visible to all players
strategy - how does a computer player respond based on its current hand and its knowledge of the game state
once you have that working, you can get develop more sophisticated strategies:
inference - what cards does the human player likely hold based on her prior actions
game tree search - how to maximize the chance of winning given what could possibly happen
then if you want to get really sophisticated, you can start looking into opponent modeling
Macek's answer is good as far as for setting up a deck.
You also asked about other entities.
You probably want four "Players". Each player might be either human or machine controlled.
To implement a human player, you interface with the screen/mouse/keyboard; to implement the machine controlled players, you have a hand and you can see some central cards on a table (all the players need to know of a central table that holds any cards that would be on the table).
From there the logic is based on what game you are playing.
Once your "Player" (AI) gets the turn (for instance, a takeTurn method is called on your player object), it should examine its cards and make the proper decisions--taking cards from stacks on the table or placing cards from its hand onto the table. (The table almost certainly has at least two stacks a player can access--"Draw" and "Discard".)
When a Human player has his takeTurn method called, it should interface with the screen--updating the player's hand, allowing him to draw and discard.
When each player is done with his turn, it should return. It can't directly call the next player (otherwise you'd start to build up a stack), so you need some form of turn control that can call the players in order. This centralized control also prevents players from knowing about each other, they shouldn't really need to (one of the best OO design tactics is that each object should know as little about other objects as possible).
...Still thinking... I may add more...
I'm not sure what sort of card game you want to build but the most common way of building this sort of AI is generating a tree of possible options. I don't think there's a library to do it as such but ruby can do trees easily.
The aim is to have a root node which is the present time and then each child node is a possible action. Then the child of each possible action is the next possible action. From there you can build a tree of every possible outcome. All that remains is to select the outcome you like.
Where you don't have all the information (ie can't see your opponents cards) you simulate it. By simulate I mean guess. The average of all the simulations/guesses will give you a good idea of which tree branches are 'likely to be the best'.
If you can do all that you're well on the way (and it's a really good exercise), there's hundreds of AI articles about, google will be your friend. The only problem with the approach I described is it can be desperately slow but there are many clever techniques to speed it up like transposition tables, alpha-beta pruning etc... which I don't suggest you look up quite yet.
Something very simple to get you started:
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