3
$\begingroup$

This question already has an answer here:

Part I - Without package

I have created a function to compute a bridge score.

computeScore[level_, denomination_, vul_, dbl_, tricks_] := Module[
  {
   trickScore = 0,
   trickMultiplier = 2^dbl,
   numberEffectiveTricks,
   trickValue = {20, 30, 30},
   gameBid,
   partialBonus = 0,
   gameBonus = 0,
   gameValue = {300, 500},
   gameTricks = {5, 4, 3},
   slamBonus = 0,
   slamValue = {500, 750},
   insult = 0,
   overtrickScore = 0,
   overtrickValue = {100, 200}, (* doubled overtricks non-vul/vul *)
   undertrickScore = 0,
   undertrickValue = {50, 100},
   doubled = False,
   redoubled = False,
   suitIndex,
   total
   },

  (* Convert the denomination argument into an index into a suit array *)

  suitIndex = Which[
    SameQ[denomination, $c] || SameQ[denomination, $d],
    1,
    SameQ[denomination, $h] || SameQ[denomination, $s],
    2,
    SameQ[denomination, $nt],
    3
    ];

  (* Take two paths depending upon whether
    the contract made or was set *)

  If[tricks >= level,
   (* contract made *)

   numberEffectiveTricks = 2^dbl*level;

   trickScore = numberEffectiveTricks*trickValue[[suitIndex]] +
     10*trickMultiplier*Floor[suitIndex/3];

   overtrickScore = (tricks - level)*
     (UnitStep[-dbl]*trickValue[[suitIndex]] + 
       dbl*overtrickValue[[vul + 1]]);

   insult = 50*dbl;

   gameBid = UnitStep[numberEffectiveTricks - gameTricks[[suitIndex]]];

   partialBonus = (1 - gameBid)*50;

   gameBonus = gameBid*gameValue[[vul + 1]];

   slamBonus = (level - 5)*UnitStep[level - 5]*slamValue[[vul + 1]];

   total = 
    trickScore + overtrickScore + insult + partialBonus + gameBonus + 
     slamBonus
   ,
   (* contract was set *)

   undertrickScore = With[
     {
      undertricks = level - tricks
      },

     If[dbl == 0,
      -50*undertricks*(vul + 1),
      dbl*If[vul == 0,
        -100 - 200*(undertricks - 1)*UnitStep[undertricks - 2] -
                     100*(undertricks - 3)*UnitStep[undertricks - 4],
        -200 - 300*(undertricks - 1)*UnitStep[undertricks - 2]
        ]
      ]
     ]
   ]
  ]

It works fine. Here is an example where the contract is 3 spades, not vulnerable, not doubled, making 3.

computeScore[3, $s, 0, 0, 3]
140

Note that I am using a set of undefined symbols inside the module, $c thru $nt to represent clubs through notrump.

Part II - Inside package

Now close Mathematica and start a new notebook.

Use the same function inside a package. To save space I won't re-copy the function. The new notebook looks like:

BeginPackage["bridgeScore`"]

computeScore::usage = 
  "Returns the score given the level, suit, vulnerability (0/1),\n\
doubled status (0/1/2) and the number of tricks";

Begin["`Private`"]

Copy computeScore code here ...

End[]

EndPackage[]

Now when I execute I get an error

computeScore[3, $s, 0, 0, 3]

Part::pkspec1: The expression Null cannot be used as a part specification.

I have traced it down to the statement where denomination is tested

suitIndex = Which[
   SameQ[denomination, $c] || SameQ[denomination, $d],
   1,
   SameQ[denomination, $h] || SameQ[denomination, $s],
   2,
   SameQ[denomination, $nt],
   3
   ];

For some reason this works fine until you place it in a package.

I don't have a clue why, can any one help?

$\endgroup$

marked as duplicate by Kuba May 3 '17 at 20:44

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • $\begingroup$ Why use a dollar sign at all? $\endgroup$ – David G. Stork Oct 4 '16 at 22:08
  • $\begingroup$ Also, where you use Which, you might be able to shorten your code by using Switch. $\endgroup$ – Jason B. Oct 4 '16 at 22:09
  • 4
    $\begingroup$ at least closely related: Derivative in my package return 0 $\endgroup$ – Kuba Oct 5 '16 at 5:25
5
$\begingroup$

The issue is that you are calling the function with Global`$s, but the function definition uses the private version of the symbol $s. Notice it works if you provide the context manually,

computeScore[3, bridgeScore`Private`$s, 0, 0, 3]
(* 140 *)

So your issue doesn't really have anything to do with the dollar sign. All you need to do is put this line before the line containing Begin["`Private`"]

{$s,$c,$d,$h,$nt}

This puts all of these variables in the correct context. But be aware that if someone puts the same value to two of these constants, your function breaks.

computeScore[3, $s, 0, 0, 3]
$s = $c = 3;
computeScore[3, $s, 0, 0, 3]
(* 140 *)
(* 110 *)

One way around this would be to use strings for the suit tokens. You can substitute "hearts" for $h, and so on, directly in your code and it will work the same. You won't have to worry about context or whether they have numeric values.

$\endgroup$
  • $\begingroup$ Thank you, I understand perfectly my mistake. Yes, using strings appears to be the safest course. $\endgroup$ – Jack LaVigne Oct 5 '16 at 8:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.