|
Post by Rod on Oct 26, 2019 8:41:39 GMT
Ok, a bit of a challenge. Rummikub is quite a fun game and ideal for a computer project though complex. So, one step at a time. The game starts with each player getting fourteen random chips. To start playing they must build runs or sets totalling thirty points or more. (Jokers at a later time) If they cant play they receive another random chip and await another turn.
There are 106 chips in total red, black, orange, and cyan colored. They are numbered 1 to 13 and two jokers. Runs must be of the same color, red 10, red 11, red 12 for example. Sets must not contain same colors, red 13 cyan 13 black 13 for example
Here we have the players first fourteen chips, the task is to pick out the best run and set combination possible. It is possible to get 30 in this hand but the challenge is to maximise the play and state what should be played.
dim p1(50,5) cnum=1 'sprite name cval=2 'face value ccol=3 'color
p1(1,cnum)=1 p1(2,cnum)=2 p1(3,cnum)=3 p1(4,cnum)=4 p1(5,cnum)=5 p1(6,cnum)=6 p1(7,cnum)=7 p1(8,cnum)=8 p1(9,cnum)=9 p1(10,cnum)=10 p1(11,cnum)=11 p1(12,cnum)=12 p1(13,cnum)=13 p1(14,cnum)=14 p1(1,ccol)=1 p1(2,ccol)=1 p1(3,ccol)=1 p1(4,ccol)=1 p1(5,ccol)=4 p1(6,ccol)=3 p1(7,ccol)=3 p1(8,ccol)=3 p1(9,ccol)=2 p1(10,ccol)=2 p1(11,ccol)=1 p1(12,ccol)=2 p1(13,ccol)=3 p1(14,ccol)=4 p1(1,cval)=5 p1(2,cval)=2 p1(3,cval)=3 p1(4,cval)=1 p1(5,cval)=13 p1(6,cval)=6 p1(7,cval)=7 p1(8,cval)=8 p1(9,cval)=10 p1(10,cval)=11 p1(11,cval)=12 p1(12,cval)=12 p1(13,cval)=12 p1(14,cval)=12 player1holds=14
|
|
|
Post by zzz000abc on Oct 27, 2019 14:35:39 GMT
Hi, here is my solution (not graphical)
'rummikub 'each pack contains 13*4 =52 cards/chips so 2 packs contain total 104 cards/chips 'excluding jokers. 'r,b,o,c stand for red,black,orange and cyan respectively. 'for every possible run 1 the player gets 1 point and for every set 1 point 'for example if player gets 10R,11R,12R,13R , 'for 10r11r12r 1run and for 11r12r13r 1run total 2 points he gets [buildPack] n=4 'number of packs c$="R B O C" global s$,tChips tChips=n*52 for k=1 to n for i=1 to 13 for j=1 to 4 s$=s$+str$(i)+word$(c$,j)+chr$(32) next next next s$=trim$(s$):tmps$=s$ ' print s$ [choosePlayers] k=0 while 1 input"how many players ? ";k if int(k)=k and k>1 and k<(2*n)+1 then exit while else _ print "enter an integer between 1 and ";(2*n)+1 wend dim p$(k),run$(k),set$(k),win$(k),rwin(k),swin(k),win(k),t(4),tr$(4) [distribute] s$=tmps$:tChips=n*52 for i=1 to k p$(i)="":run$(i)="":set$(i)="" win$(i)="":rwin(i)=0:swin(i)=0 win(i)=0 if i<5 then t(i)=0:tr$(i)="" next print s$ for i=1 to k call getChip i next for i=1 to k print: print "player ";i;" :": print for j=1 to 14 print word$(p$(i),j), if j mod 4=0 then print next print : print "-----------------------------------------------------": print next [chkRun] for ii=1 to k l$=p$(ii) for i=4 to 13 for j=1 to 14 w$=word$(l$,j) if val(w$)=i and right$(w$,1)="R" then tr$(1)=tr$(1)+w$+chr$(32):t(1)=t(1)+1 if val(w$)=i and right$(w$,1)="B" then tr$(2)=tr$(2)+w$+chr$(32):t(2)=t(2)+1 if val(w$)=i and right$(w$,1)="O" then tr$(3)=tr$(3)+w$+chr$(32):t(3)=t(3)+1 if val(w$)=i and right$(w$,1)="C" then tr$(4)=tr$(4)+w$+chr$(32):t(4)=t(4)+1 next next
for i=1 to 4 if t(i)<3 then goto[xyz] for j=2 to t(i)-1 x$=word$(tr$(i),j-1):y$=word$(tr$(i),j):z$=word$(tr$(i),j+1) if val(y$)-val(x$)=1 and val(z$)-val(y$)=1 and _ val(x$)+val(y$)+val(z$)>29 then _ run$(ii)=x$+y$+z$+";":rwin(ii)=rwin(ii)+1:win(ii)=win(ii)+1 next [xyz] next
call chkset 1,2,3,ii call chkset 1,2,4,ii call chkset 1,3,4,ii call chkset 2,3,4,ii
print "player : ";ii if rwin(ii)=0 then _ print "no runs ":goto [chkset2] [chkrun2] for i =1 to rwin(ii) if word$(run$(ii),i,";")<>"" then _ print word$(run$(ii),i,";"),"1" next [chkset2] if swin(ii)=0 then _ print "no sets ":goto [tscore] for i =1 to swin(ii) if word$(set$(ii),i,";")<>"" then _ print word$(set$(ii),i,";"),"1" next [tscore] print "total score : ";win(ii) for i=1 to 4 tr$(i)="":t(i)=0 next
next
for i=1 to k if win(i)=0 then t=t+1 next if t<>k then goto[end1] input"press any key to continue";a t=0 :goto[distribute] [end1] end
'---------------------------------------------------------- 'Subs '---------------------------------------------------------- sub getChip pNum k=0 [xyz] r=int(rnd(1)*tChips)+1 l$=l$+word$(s$,r)+chr$(32) for i=1 to r-1 ss$=ss$+word$(s$,i)+chr$(32) next for i=r+1 to tChips ss$=ss$+word$(s$,i)+chr$(32) next tChips=tChips-1:k=k+1 s$=trim$(ss$) if k<>14 then goto[xyz] p$(pNum)=trim$(l$) end sub
sub chkset a,b,c,p for i1=1 to t(a) for i2=1 to t(b) for i3=1 to t(c) x$=word$(tr$(a),i1):y$=word$(tr$(b),i2):z$=word$(tr$(c),i3) if right$(x$,1)<>right$(y$,1) and _ right$(x$,1)<>right$(z$,1) and _ val(x$)+val(y$)+val(z$)>29 then set$(p)=set$(p)+x$+y$+z$+";":swin(p)=swin(p)+1:win(p)=win(p)+1 end if next next next
end sub
|
|
|
Post by Rod on Oct 27, 2019 15:01:11 GMT
Ok, cool stuff. But the point scoring is off for Rummikub. If the player has a run o11,o12,o13 the score is 36 if they also had a set of b1,c1,r1 they would get an additional 3 bringing the total score to 39. The chips get face value when scoring.
The initial task is to get the maximum value out of the hand we have. Jokers and Table play are a bit down the line.
Say we had o12,r12,c12 and b12 we have a set of four worth 48. But we also have c13 and c11. What we want is o12,r12,b12 AND c13,c12,c11 scores 36+36=72! Tricky or what? But that's the game.
|
|
|
Post by B+ on Oct 27, 2019 15:46:43 GMT
Man! took me all night to spot straights and groups and points for them. Now to handle group intersect straight. Yikes, and then there is duplicate tile problem and then 2 jokers!
But I can check if a set is good and score it.
|
|
|
Post by Rod on Oct 29, 2019 20:03:28 GMT
By no means finished or debugged but you will see my strategy. I start with the high numbers and make as many runs as I can and then look for the sets remaining. I can see flaws and possible bugs. More testing required but it is a start. This just assesses the humans hand , it will assess every players hand. Once tested a bit more (and jokers included) I will move on to table play, about 2^4 more complex!
'deal the first fixed format hand for testing, 14 chips dim p1(50,5)
cnum=1 'sprite name cval=2 'face value ccol=3 'color csta=4 'playing status cord=5 'ordering column p1(1,cnum)=1 p1(2,cnum)=2 p1(3,cnum)=3 p1(4,cnum)=4 p1(5,cnum)=5 p1(6,cnum)=6 p1(7,cnum)=7 p1(8,cnum)=8 p1(9,cnum)=9 p1(10,cnum)=10 p1(11,cnum)=11 p1(12,cnum)=12 p1(13,cnum)=13 p1(14,cnum)=14 p1(1,ccol)=4 p1(2,ccol)=1 p1(3,ccol)=1 p1(4,ccol)=1 p1(5,ccol)=4 p1(6,ccol)=3 p1(7,ccol)=3 p1(8,ccol)=3 p1(9,ccol)=2 p1(10,ccol)=2 p1(11,ccol)=1 p1(12,ccol)=2 p1(13,ccol)=3 p1(14,ccol)=4 p1(1,cval)=11 p1(2,cval)=2 p1(3,cval)=3 p1(4,cval)=1 p1(5,cval)=13 p1(6,cval)=6 p1(7,cval)=7 p1(8,cval)=8 p1(9,cval)=1 p1(10,cval)=1 p1(11,cval)=12 p1(12,cval)=12 p1(13,cval)=12 p1(14,cval)=12 player1holds=14
[assesshand] 'put hand in value/color and search for jokers for n= 1 to 14 p1(n,cord)=100*p1(n,cval)+p1(n,ccol) next sort p1(),1,player1holds,cord gosub [showhand]
'joker will be ranked first j=0 if p1(player1holds,cval)=30 then j=1 if p1(player1holds-1,cval)=30 then j=2 cp=player1holds-j
'find runs 'put hand in color/value order for n= 1 to player1holds p1(n,cord)=100*p1(n,ccol)+p1(n,cval) next sort p1(),1,player1holds,cord gosub [showhand]
'check for runs 11 12 13 example in decreasing value order 'a set is three or more consecutively numbered same color 'chips or two plus a joker while cp>0 op=cp nm=0 m=p1(cp,cval) c=p1(cp,ccol) while cp>0 and p1(cp,cval)=m and p1(cp,ccol)=c nm=nm+1 cp=cp-1 m=m-1 wend if nm>2 then 'go back and set played status cp=op m=p1(cp,cval) c=p1(cp,ccol) while cp>0 and p1(cp,cval)=m and p1(cp,ccol)=c print p1(cp,ccol),p1(cp,cval),p1(cp,csta) p1(cp,csta)=nm cp=cp-1 m=m-1 wend end if
wend
'sets of four or three 'put hand in value/color for n= 1 to player1holds p1(n,cord)=100*p1(n,cval)+p1(n,ccol) next sort p1(),1,player1holds,cord gosub [showhand]
cp=player1holds-j
'check for sets 13,13,13,13 for example in decreasing value order 'a set is three or more different colored, same value chips 'or two plus a joker, if you have three no point in wasting the joker while cp>0 op=cp nm=0 m=p1(cp,cval) c=0 while cp>0 and p1(cp,cval)=m if p1(cp,ccol)<>c then nm=nm+1 c=p1(cp,ccol) cp=cp-1 wend if nm>2 then 'go back and set played status cp=op m=p1(cp,cval) c=0 while cp>0 and p1(cp,cval)=m if p1(cp,ccol)<>c and p1(cp,csta)=0 then p1(cp,csta)=nm print p1(cp,ccol),p1(cp,cval),p1(cp,csta) end if c=p1(cp,ccol) cp=cp-1 wend end if
wend
'runs 'find highest value play wait
[showhand] for n= 1 to player1holds if p1(n,ccol)=1 then print "red "; if p1(n,ccol)=2 then print "black "; if p1(n,ccol)=3 then print "cyan "; if p1(n,ccol)=4 then print "orange "; print p1(n,cval),p1(n,csta) next return
|
|
|
Post by B+ on Oct 31, 2019 7:50:41 GMT
AIMeld function working pretty well finding Straight and Group Sets independently and then resolve problems when an intersecting tile is used in both Sets. I have added more tiles than original 14 to test dilemmas AI has to decide between using a tile in a Straight or a Group.
'Rummikub AI 1 Rod's starter B+ 2019-10-26
dim p1(50,5), cO$(13, 10) 'Rod's structure, and mine cO$() for the AI = c for computer O for Order of Strings AKA computer's rack '----------------- Rod's structure cnum=1 'sprite name cval=2 'face value ccol=3 'color p1(1,cnum)=1 p1(2,cnum)=2 p1(3,cnum)=3 p1(4,cnum)=4 p1(5,cnum)=5 p1(6,cnum)=6 p1(7,cnum)=7 p1(8,cnum)=8 p1(9,cnum)=9 p1(10,cnum)=10 p1(11,cnum)=11 p1(12,cnum)=12 p1(13,cnum)=13 p1(14,cnum)=14 p1(1,ccol)=1 p1(2,ccol)=1 p1(3,ccol)=1 p1(4,ccol)=1 p1(5,ccol)=4 p1(6,ccol)=3 p1(7,ccol)=3 p1(8,ccol)=3 p1(9,ccol)=2 p1(10,ccol)=2 p1(11,ccol)=1 p1(12,ccol)=2 p1(13,ccol)=3 p1(14,ccol)=4 p1(1,cval)=5 p1(2,cval)=2 p1(3,cval)=3 p1(4,cval)=1 p1(5,cval)=13 p1(6,cval)=6 p1(7,cval)=7 p1(8,cval)=8 p1(9,cval)=10 'orig 10 change to 13 to check top end of straight p1(10,cval)=11 p1(11,cval)=12 p1(12,cval)=12 p1(13,cval)=12 p1(14,cval)=12 player1holds=14
'convert Rod's data to my structure for i = 1 to player1holds 'load player1's rack call deck2Computer p1(i, ccol), p1(i, cval) next
'add 4_13 to deck and create another decision, kill a set or a group call deck2Computer 4, 11
'create another test dilemma, a group added onto the bottom of a straight call deck2Computer 1, 6 call deck2Computer 2, 6
'create a save, a duplicate can be substituted in 2 of same intersecting tiles! call deck2Computer 2, 12 'comment out the above after confirm the test
'test removing a intersecting tile from bottom of run call deck2Computer 3, 9
'show the computer's hand print "Computer's Hand:" for row = 1 to 10 for col = 1 to 13 locate col * 5, row + 1: print cO$(col, row); next next print:print "-------------------------------------------------------------------------" 'why is the above line print so far down the screen??? 'BECAUSE I AM PRINTING ROWS WHERE REDUNDANT TILES WOULD GO
madeMeld = AImeld() if madeMeld > 29 then print "Meld was made." 'show board else print "Can't make meld, AI draws a tile." 'draw tile end if print "Run is done." end
SUB deck2Computer clr, number 'this is better than a sort, maybe tile$ = str$(clr);"_";left$(str$(number);" ", 2) IF cO$(number, clr) <> "" THEN cO$(number, clr + 6) = tile$ ELSE cO$(number, clr) = tile$ END SUB
function AImeld() 'return 0 if meld was not made else return 1 = meld was made and the sets added to table 'look for straights for r = 1 to 4 scan c = 1 : quit = 0 do while quit = 0 and c < 14 scan while cO$(c, r) = "" scan c = c + 1 if c > 11 then quit = 1 : exit while wend if c < 12 then cStart = c print "cStart ";cStart while cO$(c, r) <> "" scan c = c + 1 if c = 14 then quit = 1 : exit while wend if c = 14 then cEnd = 13 else cEnd = c - 1 print "cEnd ";cEnd if cEnd - cStart + 1 > 2 then print "Straight of ";cEnd-cStart+1, set$ = "": points=0 for cs = cStart to cEnd set$ = set$ + str$(r)+"_"+left$(str$(cs);" ", 2) points = points + cs next if sSets$ = "" then sSets$ = set$ else sSets$ = sSets$ + "," +set$ if sp$ = "" then sp$ = str$(points) else sp$ = sp$+","+ str$(points) ns = ns + 1 print set$;" Points ";points end if if c > 11 then quit = 1 else exit do end if 'print "c at end of loop ";c loop next
'ok now fer the groups for col = 1 to 13 count = 0 if cO$(col, 1) <> "" then count = count + 1 if cO$(col, 2) <> "" then count = count + 1 if cO$(col, 3) <> "" then count = count + 1 if cO$(col, 4) <> "" then count = count + 1 'print "count ";count if count > 2 then set$ = "" if cO$(col, 1) <> "" then set$ = set$ + str$(1)+"_"+left$(str$(col);" ", 2) if cO$(col, 2) <> "" then set$ = set$ + str$(2)+"_"+left$(str$(col);" ", 2) if cO$(col, 3) <> "" then set$ = set$ + str$(3)+"_"+left$(str$(col);" ", 2) if cO$(col, 4) <> "" then set$ = set$ + str$(4)+"_"+left$(str$(col);" ", 2) points = count * col if gSets$ = "" then gSets$ = set$ else gSets$ = gSets$ + "," + set$ if gp$ = "" then gp$ = str$(points) else gp$ = gp$+","+str$(points) ng = ng + 1 print count;" Count Group ";set$,"Points ";points end if next print "Straight Sets: ";sSets$ print "Straight Set Points:";sp$ print "Group Sets: ";gSets$ print "Group Set Points: ";gp$ print "Now proceeding to check which tiles are used twice, once in Straight and again in Group."
'----------------------------------------------------------------------- Straight and Group Sets found
' Part 2 look for intersects if any, this may be tricky but at least no joker's to worry about
'find intersects of cards, any clear and free sets set asside 'if a card intersects see if a redundant for that card exits 'then see if the straight or group has more than 3 cards and if overlap is not needed 'here having setOK could come in handy 'OK I think there will be more tiles use to make groups than straights so go through straights
'recap 'ns = number straights, sSets$ = straight Sets, sp$ = straights points 'ng = number of groups, gSets$ = Group sets, gp$ group points ' the strings are delimited by commas if ns > 0 and ng > 0 then 'otherwise no intersects possible
'check if any straight tile intersects with a group tile for si = 1 to ns 'ns = number of sets, si = set index 'pull out each straights set and check each tile against all tiles in Group sets sSet$ = word$(sSets$, si, ",") 'a straight set print:print : Print "sSet$ = *";sSet$;"*" nsTiles = LEN(sSet$) / 4 'has this many tiles = number straight Tiles for sti = 1 to nsTiles ' sti = straight tile index sTile$ = mid$(sSet$, sti * 4 - 3, 4) 'sTile$ straight tile name Print "sTile$=*";sTile$;"* "; for gi = 1 to ng 'gi = group indes ng number of groups gSet$ = word$(gSets$, gi, ",") 'group set$ ngTiles = len(gSet$) / 4 ' number of group tiles for gti = 1 to ngTiles 'group tile index gTile$ = mid$(gSet$, gti * 4 - 3, 4) 'group tile name print " gTile$=*";gTile$;"*"; if gTile$ = sTile$ then ' intersect are the names the same? Print:Print "Found intersecting tile: *";gTile$;"*" 'is there a second tier card iClr = val(left$(gTile$, 1)) : iNum = val(mid$(gTile$, 3, 2)) if cO$(iNum, iClr + 6) = "" then ' have no duplicate 'maybe one of the sets can tolerate removal of the tile 'a group with 4 tiles could loose any one of it's tile if (nsTiles > 3 and sti = 1) or (nsTiles > 3 and sti = nsTiles) then 'remove tile from the straight set place = instr(sSets$, gTile$) sSets$ = mid$(sSets$, 1, place - 1);mid$(sSets$, place + 4) 'take off point val too pts = val(word$(sp$, si, ",")) pts = pts - iNum b$ = "" for w = 1 to ns if w <> si then if b$ = "" then b$ = word$(sp$, w, ",") else b$ = b$;",";word$(sp$, w, ",") else if b$ = "" then b$ = str$(pts) else b$ = b$;",";str$(pts) end if next sp$=b$ print "Removed intersecting tile from Straight Set > 3 tiles to save Group Set."
else 'a group with more than 3 tiles could loose the tile if ngTiles = 4 then 'loose the tile from the group Print "Remove from group of 4" place = instr(gSets$, gTile$) 'print "place found at ";place gSets$ = mid$(gSets$, 1, place - 1);mid$(gSets$, place + 4) pts = 3 * iNum b$ = "" for w = 1 to ng if w <> gi then if b$ = "" then b$ = word$(gp$, w, ",") else b$ = b$;",";word$(gp$, w, ",") else if b$ = "" then b$ = str$(pts) else b$ = b$;",";str$(pts) end if next gp$ = b$ print "Removed intersecting tile from Group of 4 Set to save Straight Set."
else ' OK then we have to kill a set 'if the group has less points than the set kill the group else kill the set if val(word$(gp$, gi, ",")) < val(word$(sp$, si, ",")) then print "Removing the Group Set: ";word$(gSets$, gi, ","); print " to preserve the Straight Set: ";word$(sSets$, si, ",") bg$ = "" : bgp$ = "" for w = 1 to ng if w <> gi then if bg$ = "" then bg$ = word$(gSets$, w, ",") else bg$ = bg$;",";word$(gSets$, w, ",") if bgp$ = "" then bgp$ = word$(gp$, w, ",") else bgp$ = bgp$;",";word$(gp$, w, ",") end if next gSets$ = bg$ : gp$ = bgp$ : ng = ng - 1 else 'kill the set print "Removing the Straight Set: ";word$(sSets$, si, ","); print " to preserve the Group Set: ";word$(gSets$, gi, ",") bs$ = "" : bsp$ = "" for w = 1 to ns if w <> si then 'rebuild sets with one less and rebuild set points with one less if bs$ = "" then bs$ = word$(sSets$, w, ",") else bs$ = bs$;",";word$(sSets$, w, ",") if bsp$ = "" then bsp$ = word$(sp$, w, ",") else bsp$ = bsp$;",";word$(sp$, w, ",") end if next sSets$ = bs$ : sp$ = bsp$ : ns = ns - 1 end if end if ' group > 3 end if 'of remedies starting with removal of tile at one end of straight else print "OK have a duplicate tile." end if ' remedies for intersecting if no duplicate tile in rack end if 'matching intersecting tile next ' group set tile next ' group set next ' tile from straight set next 'straight set number end if
print:print 'Recap straight sets found then group sets then if intersect tile in both a straight set and group set it is fixed ' now we need to see if enough points for meld points = 0 for i = 1 to ns points = points + val(word$(sp$, i, ",")) next for i = 1 to ng points = points + val(word$(gp$, i, ",")) next AImeld = points ' layout sets
print "Straight Sets: ";sSets$ print "Straight Set Points:";sp$ print "Group Sets: ";gSets$ print "Group Set Points: ";gp$ print "Total Points: ";points end function
|
|
|
Post by Rod on Oct 31, 2019 8:37:08 GMT
Very cool. I like the table displaying the hand, it aids understanding.
|
|
|
Post by B+ on Oct 31, 2019 15:08:11 GMT
Thanks Rod, you have helped with clue how to handle Jokers next (for meld) = try to use one (or both what luck!) to fill an inside straight when looking for straights. Otherwise it could be tacked on anywhere at anytime, best to highest straight or group point set (probably better, there is a lower limit to tacking onto groups).
I am considering switching tile structure to [number_color] so logically consistent with a graphics drawn tile (x, y) or a PRINT at LOCATE column, row and the displaying of hands.
|
|
|
Post by B+ on Oct 31, 2019 15:39:00 GMT
Ah, a potential logic bug brewing because I am changing the gSets$, sSets$ strings and ns and ng counts of those sets while using those strings and numbers while hunting for tile intersects between Straight and Group Sets. I better keep counts the same and replace the sets$ with empty strings as placeholders.
What could happen? I could jump past a check... I guess I won't try to access an array (cO$() the Computer's hand arranged into 13x10 array) outside it's limit, so compiler won't error out but will start checking wrongly.
Hey has anyone changed the stop value for variable in a FOR loop while inside the loop processing data? Would it stop at new value of continue with old?
forStop = 10 for i = 1 to forStop print i forStop = 5 next
It continues with old.
Oh, there might not be a problem of skipping over a check.
No? I am still removing a set from a string or a tile from a set... hmm, I know WORD$ won't complain of indexes out of bounds
|
|