Score counter for Skat

11

0

You task is to write a small program, that counts the points of a Skat hand. A Skat deck has cards 7 to 10, Jack, Queen, King and Ace (called Unter, Ober, König and Daus). We use the German suits, which have Acorns, Leaves, Hearts and Bells instead of Clubs, Spades, Hearts and Diamonds. The points are determined by the number on the card:

  • 7, 8 and 9 are 0 points
  • Unter is 2 points
  • Ober is 3 points
  • König is 4 points
  • 10 is 10 points
  • Daus is 11 points.

Input / Output

The input format consists of two symbols, the first one represents the value, while the second one stands for the suit:

  • 7, 8 and 9 stand for them self
  • 0 (zero) stands for 10
  • Unter, Ober, König and Daus are named after their first letters (U, O and D)
  • The same for Acorns, Leaves, Hearts and Bellys (A, L, H and B)

The input is a single line of cards, separated by a single whitespace. You may take the input from anywhere, command line arguments are okay too. The output is the value of the hand, either printed out or returned as an exit code. The output of your program must show an error, if any card appears twice in the hand. (So 7A 0L 7A must return an error instead of 10). It is also okay to quit with an exit code of 255 instead of showing an error, if this is the default way of your program to output the result.

Examples

  • 7A 8A 9A UA OA KA 0A DA 7L 8L 9L UL OL KL 0L DL 7H 8H 9H UH OH KH 0H DH 7B 8B 9B UB OB KB 0B DB gives 120
  • 7A 8L 0K DB 8L gives an error
  • UA OB DL KH gives 20

Rules

  • Code golf: The shortest code wins
  • Usual code golf rules apply
  • The program has to work for all hands, not just the examples
  • GIGO: If the input is invalid, the output may be arbitrary

FUZxxl

Posted 2011-06-06T17:15:42.213

Reputation: 9 656

2I believe you mean "bells", not "bellys". Very different, that. – boothby – 2012-05-30T03:37:39.650

Is additional output to stderr (e.g. warnings) ok? – Ventero – 2011-06-09T09:02:54.500

@Ventero: Yes, it is. Any way to err out is okay, but it must be clearly visible to the user, that there is an error. – FUZxxl – 2011-06-09T17:40:36.233

Jack, Queen, and Ace are called Unter, Ober, King and Daus? Is a king supposed to be in there? – Ry- – 2011-06-13T19:51:03.283

@minitech No, it is not. – FUZxxl – 2011-06-14T06:45:34.450

Answers

2

APL (54 48)

There has to be a shorter way of selecting the card value, but I don't see it.

(+/12-'D0.....KOU.'⍳⊃¨A)÷A≡∪A←↓A⍴⍨2,⍨2÷⍨⍴A←⍞~' '

You get a DOMAIN ERROR if there's a duplicate card.

Explanation:

  • A←⍞~' ': store () into A a line of user input () without (~) the spaces.
  • 2,⍨2÷⍨⍴A: a two-element list, containing the length of () A divided by (÷⍨) 2, followed by (,⍨) the number 2. (So, if the input is UA OB DL KH the list is (4, 2)).
  • ↓A⍴⍨: define a matrix (), with the dimensions of that list, containing the values of A. Then join the elements of its rows together (), giving a list of lists, for example ['UA','OB','DL','KH'].
  • A←: Store this list in A.
  • A≡∪A: ∪A is the list of unique elements in A. If this is equal to A, there are no duplicates and this returns 1, otherwise 0.
  • ÷: divide what's on the left (which does the actual calculation) by the result of the equality test. So if there are no duplicates, the score is unchanged, and if there are duplicates you get a DOMAIN ERROR because of the division by zero.
  • ⊃¨A: A list giving the first element () of each element (¨) of A. So this drops the suit letter, leaving the score letter. (UODK)
  • 'D0.....KOU.'⍳: gives the index of each of the score letters in this string, returns 12 for values not in the string. (10 9 1 8)
  • +/12-: subtract all of these from 12, and then add them together. (2 + 3 + 11 + 4 = 20)

marinus

Posted 2011-06-06T17:15:42.213

Reputation: 30 224

I totally missed that your answer is the shortest. – FUZxxl – 2015-04-08T11:08:10.727

10

Ruby 1.9, 52 characters

Input via command line arguments. I'm assuming the error message when having duplicate cards doesn't matter, so it just complains about an eval/type conversion error.

p eval$*.uniq!||$*.map{|i|"..UOK#$<.0D"=~/#{i}?/}*?+

Example usage:

$ ruby1.9 skatscore.rb 7A 8A 9A UA OA KA 0A DA 7L 8L 9L UL OL KL 0L DL 7H 8H 9H UH OH KH 0H DH 7B 8B 9B UB OB KB 0B DB
120

$ ruby1.9 skatscore.rb 7A 7A
skatscore.rb:1:in `eval': can't convert Array into String (TypeError)
    from skatscore.rb:1:in `<main>'

Ventero

Posted 2011-06-06T17:15:42.213

Reputation: 9 842

On the one hand I think the undefined variable error for duplicate cards is kinda lame. On the other hand, it doesn't break the rules, so it's kinda clever. – Igby Largeman – 2011-06-08T02:33:45.280

1@Charles: Since the spec only calls for an error, I guess what error exactly is pretty much irrelevant. And if there are short methods of producing any error, that should be fine, I guess. – Joey – 2011-06-09T23:01:28.277

6

Scala, 87 82 characters

args.distinct(args.size-1);println(args.map(a=>1+" UOK     0D".indexOf(a(0))).sum)

Throws an exception on repeated cards.

Rex Kerr

Posted 2011-06-06T17:15:42.213

Reputation: 903

4

Haskell, 122 108 107 characters

import List
main=interact$f.words
f x|nub x==x=show$sum$map(maybe 0 id.(`elemIndex`"  UOK     0D").head)x

hammar

Posted 2011-06-06T17:15:42.213

Reputation: 4 011

error"" is shorter than undefined. Save one char using interact. – FUZxxl – 2011-06-09T18:30:46.703

@FUZxxl: Using interact it won't print a newline, so I'm not sure if that's acceptable. However, I was able to save a lot more by using an incomplete pattern instead of undefined. – hammar – 2011-06-09T18:59:51.043

Where did I say, that a newline is needed? I can't remember. – FUZxxl – 2011-06-09T20:06:43.943

2

GolfScript 54 53 52

Edit 1:

I just discovered an error in the code. It did not detect duplicate cards if the duplicates were the first two in the input (because I was using the * fold operator and not the / each operator for the first loop).

Now I fixed the code and also managed to strip off 1 char in the process. Here's the new version:

' '/{1$1$?){]?}{\+}if}/2%{"UOK0D"\?).0>+.4>5*+}%{+}*

The input has to be on the stack as a string, in the specified format (example: '7A UA DA').

In case the input is valid, the program prints the total value of the cards.

In case there's at least one duplicate card, the program throws the following exception:

(eval):1:in `block in initialize': undefined method `class_id' for nil:NilClass (NoMethodError)

Edit 2:

After seeing this post on the meta site, I decided to post a description of the code. This also helped me find and fix an error. So, here goes:

# Initially, we epect the input string to be on the stack
# Example: "7A UA DA"

' '/            # split the input string by spaces
                # now we have on the stack an array of strings
                # (in our example: ["7A" "UA" "DA"])

# {1$1$?)!{\+}{]?}if}/  -> this piece of code checks for duplicate cards
#
# The trailing symbol (/) is the 'each' operator, meaning that the 
# preceding code block (enclosed in curly brackets) will be executed 
# for every cards in the previous array.
#
# Before each execution of the code block, the current card value
# is pushed on the stack.
#
# Basically what this code does is concatenate cards into a string
# and checks whether the current card is contained in the accumulated
# value.
#
# So, for each card, this is what we execute:

1$              # copies the concatenated string on top of the stack
                # (initially this is an empty string)

1$              # copies the current card on top of the stack

?               # returns (places on the stack) the 0-based index where 
                # the current card is found in the concatenated string
                # or -1 if not found

)               # increments the topmost stack value
                # Now we have 0 if the card is not a duplicate
                # or a value greater than 0 otherwise

{]?}{\+}if      # if the current stack value is non-0 (duplicate)
                # then execute the first code {]?} (generates an error)
                # Otherwise, if the card is valid, execute the {\+} block.
                # What this code does is essentially concatenate the current 
                # card value:
                #    \ -> swaps the two topmost stack values; now we have
                #         the concatenated string and the current card value
                #    + -> this is the concatenation operator

# After the previous code block finishes execution (in case the input is)
# valid, we end up having the concatenated card values on the stack
# In our example, this value is "DAUAUB7A".

# The next code fragment is the one that computes the card values
# This is the code: 2%{"UOK0D"\?).0>+.4>5*+}%{+}*

# And this is how it can be broken down:

2%              # takes only the even indexed chars from the existing string 
                # in our case, "DAUA7A" -> "DU7"
                # Only these characters are important for determining the 
                # card values.

# The following piece of code is:
# {"UOK0D"\?).0>+.4>5*+}%

# This code performs a map; it takes the individual chars,
# computes the corresponding numeric value for each of them and outputs an
# array containing those values

# This is achieved using the map operator (%) which evaluates the preceding 
# code block, delimited by curly braces, so, essentially this is the code that 
# computes the value for a card:
# "UOK0D"\?).0>+.4>5*+
# It can be broken down like this:

"UOK0D"         # pushes the "UOK0D" string on the stack
\               # swaps the two topmost stack values
                # Now, these values are: "UOK0D" and "l" 
                # (where "l" represents the current letter
                # that gives the card its value: U,O,K,0,D,7,8...)

?               # Find the index of the card's letter in the
                # "UOK0D" string.
                # Remember, this is 0-based index, or -1 if not found.

)               # increment the index value
                # Now we have the following value:
                #     1 if the card is U
                #     2 if the card is O
                #     3 if the card is K
                #     4 if the card is 0
                #     5 if the card is D
                #     0 if it is anything else

.0>+            # if the current value is greater than 0,
                # add 1 to it.

.4>5*+          # if the current value is greater than 4,
                # add 5 to it.

# Passing through these steps, we now have the following value:
#     2  if the card is U
#     3  if the card is O
#     4  if the card is K
#     10 if the card is 0
#     11 if the card is D
#     0  if it is anything else
# This is the exact value we were looking for.

# Now we have an array containing the value of each card.
# in our example, [0, 2, 11]
# The next piece of code is easy:

{+}*            # uses the * (fold) operator to add up all the
                # values in the array.

# This leaves the total value of the cards on the stack,
# which is exactly what we were looking for (0+2+11=13).

# Golfscript is awesome! :-)

Cristian Lupascu

Posted 2011-06-06T17:15:42.213

Reputation: 8 369

1

Python, 114 characters

i=input().split();print(sum(int(dict(zip('7890UOKD','000A234B'))[x[0]],16)for x in i)if len(i)<=len(set(i))else'')

Unfortunately, the index method of lists in Python raises an error if an element isn't found rather than returning a negative value, and importing defaultdict would require more characters than it would save.

JAB

Posted 2011-06-06T17:15:42.213

Reputation: 181

1

eTeX, 201 characters (not counting the two irrelevant linebreaks)

\def~#1#2{\catcode`#113\lccode`~`#1\lowercase{\def~}##1 {\ifcsname
#1##1 ~\D\fi\if\csname#1##1 ~\fi+"#2}}~70~80~90~0A~U2~O3~K4~DB\def
\a[#1]{\let~\endcsname\write6{^^J \the\numexpr#1 }\end}\expandafter\a

Used as etex filename.tex [UA OB DL KH]. Putting the argument in brackets is necessary: otherwise, eTeX has no way of determining that we reached the end of the argument list.

EDIT: as allowed in the statement of the question, incorrect input can cause (an) error. For instance, etex filename.tex [OK] crashes horribly (because K is not a valid color).

Bruno Le Floch

Posted 2011-06-06T17:15:42.213

Reputation: 1 181

Doesn't works on my machine. – FUZxxl – 2011-07-11T14:25:06.517

@FUZxxl. What is the output of etex -v? What is the error message (roughly)? The code should be put in a file (with name filename.tex, or anything else ending in .tex), and use that name in the command line etex filename.tex [<argument>]. (sorry to repost the same comment, I forgot "@FUZxxl") – Bruno Le Floch – 2011-07-11T15:05:31.473

Please look here: http://hpaste.org/48949

– FUZxxl – 2011-07-11T15:14:10.470

@FUZxxl. Thank you for your feedback. K is not a valid color, and replacing it by X in your examples removes the errors (it crashes with K because that letter has another meaning, King). I could make the error less horrible by adding \string before each ##1, but that would cost 12 more characters. – Bruno Le Floch – 2011-07-11T16:16:06.933

Sorry. I misstyped the example. It works now. Sorry. – FUZxxl – 2011-07-11T19:52:00.360

1

PowerShell, 79 80

($a=$args|sort)|%{$s+=(10,11+4..0)['0DKOU'.IndexOf($_[0])]}
$s/("$a"-eq($a|gu))

Throws »Attempted to divide by zero.« if cards appear twice.

Joey

Posted 2011-06-06T17:15:42.213

Reputation: 12 260