// Copyright 2005 Jim Radford <radford@blackbean.org>, David Caldwell <david@porkrind.org> -*- c -*-
var joker=0,ace=1,jack=11,queen=12,king=13,foundation=14;var clubs=0,diamonds=1,hearts=2,spades=3;var suit_string=['C','D','H','S'];var rank_string=['R','A','2','3','4','5','6','7','8','9','10','J','Q','K','F'];var up=1,down=0;var all_cards=[];function card(rank,suit,index,face,deck,back){this.rank=rank;this.suit=suit;this.index=index;this.deck=deck;this.face=face;this.design=back;this.red=suit==diamonds||suit==hearts;this.black=suit==clubs||suit==spades;this.string=(typeof this.rank=="number"?this.rank==joker?'J':rank_string[this.rank]:this.rank)
+(typeof this.suit=="number"?this.rank==joker?['1','2','3','4'][this.suit]:suit_string[this.suit]:"");all_cards.push(this);}
card.prototype.toString=function(){return this.string;}
card.prototype.next_in_suit=function(){if(this.rank!=king)
return this.deck[this.index+1];}
card.prototype.prev_in_suit=function(){if(this.rank!=ace)
return this.deck[this.index-1];}
card.prototype.next=function(){return this.deck[(this.index+1)%this.deck.length];}
function in_off_color_order(card1,card2){return card1.rank+1==card2.rank&&card1.red!=card2.red;}
function in_off_color_wrap_order(card1,card2){return(card1.rank==king&&card2.rank==ace||card1.rank+1==card2.rank)&&card1.red!=card2.red;}
function in_suit_order(card1,card2){return card1.rank+1==card2.rank&&card1.suit==card2.suit;}
function in_suit_wrap_order(card1,card2){return(card1.rank==king&&card2.rank==ace||card1.rank+1==card2.rank)&&card1.suit==card2.suit;}
function in_numeric_order(card1,card2){return card1.rank+1==card2.rank;}
function in_order_by(f,cards,start){for(var i=start||0;i<cards.length-1;i++)
if(!f(cards[i+1],cards[i])||cards[i].face!=up||cards[i+1].face!=up)
return false;return true;}
function score_order_by(f,cards,start){var score=function(n){var sum=function(n){return n*(n+1)/2;}
return Math.floor(sum(n)*13/sum(13));}
var count=0,run=0;for(var i=start||0;i<cards.length-1;i++)
if(f(cards[i+1],cards[i])&&cards[i].face==up&&cards[i+1].face==up){run+=run?1:2;}else{count+=score(run);run=0;}
return count+score(run);}
function init_deck(jokers,back){var deck=new Array(52);for(var i=0,s=clubs;s<=spades;s++)
for(var n=1;n<=king;n++,i++)
deck[i]=new card(n,s,i,down,deck,back);for(var s=0;s<jokers;s++,i++)
deck[i]=new card(joker,s,i,down,deck,back);return deck;}
var cardbacks={B1:"Red Florets",B2:"Red Diagonal",B3:"Blue Diagonal",B4:"Red Border",B5:"Blue Patchwork",B6:"Blue Helix",B7:"Olympic Rings",B8:"Colored Toes",B9:"France as Flag",B10:"Red Dinosaurs",B11:"Droit Au But",B12:"Linux Penguin",B13:"Rainbow",B14:"Yellow Patch"}
var deck_random_back=function(){return"B"+(1+Math.floor(Math.random()*14));};var cardback;var fresh_deck=[];on_load(function(){cardback=cardbacks[URL.param.cardback||Cookie.list.cardback]?URL.param.cardback||Cookie.list.cardback:undefined;for(var i=0;i<2;i++)fresh_deck.push(init_deck(0,cardback||deck_random_back()));});function shuffle(deck,times){if(times==undefined)
times=7;for(var i=0;i<times;i++)
for(var a in deck){var b=rand()%deck.length;var tmp=deck[a];deck[a]=deck[b];deck[b]=tmp;}
return deck;}
function shuffle_perfect(deck){for(var a=1;a<deck.length;a++){var b=rand()%(a+1);var tmp=deck[a];deck[a]=deck[b];deck[b]=tmp;}}
var cardsets={teensy:{width:30,height:43,base:"cards-teensy",screen_width:432},tiny:{width:50,height:72,base:"cards-tiny",screen_width:640},small:{width:69,height:101,base:"cards-small",screen_width:800},medium:{width:90,height:131,base:"cards-medium",screen_width:1024},large:{width:120,height:174,base:"cards-large",screen_width:1280},ludicrous:{width:208,height:303,base:"cards-ludicrous",screen_width:1920}};function css_sprite(url,width,height){this.url=url;this.width=width;this.height=height;this.selectors=[];this._css="";}
css_sprite.prototype.add_indexed=function(x,y,selector,ie6_url){this.selectors.push(selector);this._css+=selector+" {"
+"background-position:"+px(-x*this.width)+" "+px(-y*this.height)+";"
+"width:"+px(this.width)+";"
+"height:"+px(this.height)+";"
+"}\n";if(ie6_url&&(plat.ie<=7||plat.webkit<522))
this._css+=selector+" {_background: none; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+ie6_url+"');}\n";}
css_sprite.prototype.css=function(){return this.selectors.join(",")+" {background:url("+this.url+") no-repeat top left;}\n"+this._css;}
{var css="";for(name in cardsets){cardsets[name].name=name;var cardset=cardsets[name];var deck=new css_sprite(cardset.base+"/deck.png",cardset.width,cardset.height);for(var s=clubs;s<=spades;s++)
for(var r=ace;r<=foundation;r++)
deck.add_indexed(r-1,s,"."+cardset.name+"-"+rank_string[r]+suit_string[s],cardset.base+"/"+rank_string[r]+suit_string[s]+".png");css+=deck.css();var backs=new css_sprite(cardset.base+"/backs.png",cardset.width,cardset.height);for(var b=1;b<=14;b++)
backs.add_indexed(b-1,0,"."+cardset.name+"-"+"B"+b,cardset.base+"/B"+b+".png");css+=backs.css();var extra=new css_sprite(cardset.base+"/extra.png",cardset.width,cardset.height);extra.add_indexed(0,0,"."+cardset.name+"-"+"empty-pile",cardset.base+"/empty-pile.png");extra.add_indexed(1,0,"."+cardset.name+"-"+"highlight",cardset.base+"/highlight.png");extra.add_indexed(2,0,"."+cardset.name+"-"+"star",cardset.base+"/star.png");css+=extra.css();}
if(plat.ie)doc.id("piles-style").styleSheet.cssText+=css;else doc.id("piles-style").textContent+=css;}
function best_cardset(cardsets,size,size_key){var best;for(var i in cardsets)
if(!best||cardsets[i][size_key]<best[size_key])
best=cardsets[i];for(var i in cardsets)
if(cardsets[i][size_key]&&cardsets[i][size_key]<=size&&cardsets[i][size_key]>(best?best[size_key]:0))
best=cardsets[i];return best;}
var cardset_default=best_cardset(cardsets,plat.windowWidth(),"screen_width");var cardset;on_load(function(){cardset=cardsets[URL.param.cardset||Cookie.list.cardset]||cardset_default;});function cardset_pixels(d){return d*cardset.width/90;}
var Highlight=new card("highlight",undefined,undefined,up,null);card.prototype.image=function(copy){if(!this.front||copy){if(!this.front){this.front=document.createElement("div");}
this.front.className="card "+cardset.name+"-"+this.string;if(this.design){if(!this.back){this.back=document.createElement("div");}
this.back.className="card "+cardset.name+"-"+this.design;}}
if(this.img_face!=this.face||copy){var old_img=this.img;this.img=this.face==up?this.front:this.back;if(old_img&&old_img.parentNode)
old_img.parentNode.replaceChild(this.img,old_img);this.img.style["-apple-dashboard-region"]="dashboard-region(control rectangle)";this.img_face=this.face;}
return this.img;}
card.prototype.update=function(){this.image().style.position="absolute";this.image().style.width=px(cardset.width);this.image().style.height=px(cardset.height);}
card.prototype.draw=function(div,coords,z){if(this.animate&&div!=this.animate.div)return this.img;this.image().style.left=px(Math.floor(coords.x));this.image().style.top=px(Math.floor(coords.y));this.image().style.zIndex=2*z;this.update();this.coords=coords;if(!this.div||this.div!=div){this.div=div;this.div.appendChild(this.image());}
return this.img;}
function translate(a,b){return{x:a.x+b.x,y:a.y+b.y};}
function point_distance(a,b){return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
card.prototype.animate_to=function(div,p,z,callback){if(this.id)cancel_timeout(this.id);var pc=PageCoords(this.div);this.draw(document.body,translate(pc,{x:this.coords.x,y:this.coords.y}),z);this.animate={div:document.body};this.animate.destp=PageCoords(div);this.animate.destp.x+=p.x;this.animate.destp.y+=p.y;this.animate.dest={div:div,x:p.x,y:p.y,z:z};this.animate.next_frame=function(me,callback){var ox=me.animate.destp.x-me.coords.x;var oy=me.animate.destp.y-me.coords.y;if(Math.abs(ox)<5&&Math.abs(oy)<5){var d=me.animate.dest;me.animate=undefined;me.draw(d.div,d,d.z);if(callback!=undefined)callback();}else{me.draw(document.body,translate(me.coords,{x:ox/2,y:oy/2}),10000);me.id=timeout(30,function(){delete me.id;me.animate.next_frame(me,callback);});}}
this.animate.next_frame(this,callback);}
card.prototype.erase=function(){if(this.div){this.div.removeChild(this.image());this.div=undefined;}}
var base64={enc:"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@",to:function(n){return 0<=n<base64.enc.length?base64.enc.slice(n,n+1):"?";},from:function(c){return base64.enc.indexOf(c);}}
function pile_game(){this.piles=[];plat.register_key_events(document,wrap(this,function(event){switch(event.key){case'#':for(var p=0;p<this.piles.length;p++){if(this.piles[p].label){this.piles[p].div.removeChild(this.piles[p].label);delete this.piles[p].label;continue;}
this.piles[p].label=doc.el("div",{style:{position:"absolute",top:0,left:0,fontSize:cardset.height+"px",zIndex:2000,color:"yellow"}},doc.text(base64.to(p)));this.piles[p].div.appendChild(this.piles[p].label);}
return false;}
return true;}));}
pile_game.prototype=new seed_game;pile_game.prototype.update_cardset=function(cs){cardset=cardsets[cs]||cardset_default;Cookie.set("cardset",cardset==cardset_default?undefined:cs);this.update(Force);for(var i in all_cards){all_cards[i].update();all_cards[i].image(1);}}
pile_game.prototype.update_cardback=function(cb){cardback=cardbacks[cb]?cb:undefined;Cookie.set("cardback",cardback);var design=cardback||deck_random_back();for(var i in all_cards){if(all_cards[i].design){all_cards[i].design=design;all_cards[i].back=undefined;}
all_cards[i].update();all_cards[i].image(1);}
this.update(Force);}
pile_game.prototype.init=function(name){seed_game.prototype.init.call(this,name);doc.appendTo(this.control_panel,doc.el("select",{name:"cardset",onchange:wrap(this,function(event){var e=new plat.event(event);this.update_cardset(e.target.value);})},map(function(cs,i){return doc.el("option",(cardset==cs)?{value:i,id:"selected"}:{value:i},doc.text(i));},cardsets)));doc.id("selected").selected=true;doc.appendTo(this.control_panel,doc.el("select",{name:"cardback",onchange:wrap(this,function(event){var e=new plat.event(event);this.update_cardback(e.target.value);})},doc.el("option",(cardback==undefined)?{value:"undefined",id:"selected2"}:{value:"undefined"},doc.text("Random")),map(function(cb,i){return doc.el("option",cardback==i?{value:i,id:"selected2"}:{value:i},doc.text(cb));},cardbacks)));doc.id("selected2").selected=true;}
pile_game.prototype.new_game=function(seed,replay){var previous;seed=previous?previous[1]:seed;seed_game.prototype.new_game.call(this,seed);replay=replay||previous&&previous[1]==this.seed&&previous[2];for(var p in this.piles)
this.piles[p].pile.splice(0,this.piles[p].pile.length);for(var i in fresh_deck)
for(var c in fresh_deck[i])
fresh_deck[i][c].face=down;this.moves=[];if(!replay)
this.start_stats();for(var i in options)
this.stats.set("options_"+i,options[i]);var decks=[];for(var i=0;i<(this.decks||1);i++)decks.push.apply(decks,fresh_deck[i]);this.deal(shuffle(decks,this.seed==-1?0:7));this.update(true);var me=this;if(replay)this.replay(replay);}
pile_game.prototype.set_state=function(old,noupdate){this.seed=old.seed;this.moves=old.moves.slice(0);this.stats.set("moves",this.moves.join(""));for(var p in old.pile){this.piles[p].pile=[];for(var c in old.pile[p]){this.piles[p].pile[c]=old.pile[p][c].card;this.piles[p].pile[c].face=old.pile[p][c].face;}
this.piles[p].update_stats();}
if(!noupdate)
for(var p in old.pile)
for(var c in old.pile[p])
this.piles[p].pile[c].animate_to(this.piles[p].div,this.piles[p].coords(c),c);}
pile_game.prototype.get_state=function(){var old={pile:[]};for(var p in this.piles){old.pile[p]=[];for(var c in this.piles[p].pile)
old.pile[p][c]={card:this.piles[p].pile[c],face:this.piles[p].pile[c].face};}
old.seed=this.seed;old.moves=this.moves.slice(0);return old;}
function best_pile(piles,card){var dest=[];for(var p in piles)
dest.push(x={index:p,score:piles[p].accept(card)});dest.sort(function(a,b){return b.score-a.score});return dest[0].score?dest[0].index:undefined;}
pile_game.prototype.complete_generic=function(good,from){var me=this,any=0;step=function(cards){var testpile=function(from,cards){if(!cards)return;for(var c=from.top_index();c>=0&&(!from.drag_stacks||c==from.top_index());c--){var g;if((g=best_pile(good,from.pile[c]))!=undefined){from.move_to_pile(c,1,good[g],function(){step(1);});cards--;any++;timeout(250,function(){step(cards);});from.update();return cards;}}
return cards;}
for(var f in from)
for(var p in from[f])
if(from[f][p].pile.length)
cards=testpile(from[f][p],cards)};step(3);return any;}
pile_game.prototype.create_deck=function(deck,play,cards,redeal){if(redeal==undefined)redeal=true;var me=this;deck.onclick=wrap(this,function(){if(deck.pile.length==0&&play.pile.length<=cards)
return false;this.push_state();if(deck.pile.length==0&&play.pile.length>1){if(!redeal)return false;while(play.pile.length){var card=play.pop();card.face=down;deck.push(card);}
this.stats.inc("deck flipped");}
for(var i=0;i<cards&&deck.pile.length;i++){var card=deck.pop();card.face=up;play.push(card);}
deck.update(true);play.update(true);this.moves.push("*"+this.find_pile(deck));return false;});deck.accept=function(card){return false;}
deck.can_drag=function(index){return false;}
deck.xoffset=deck.yoffset=1;deck.maxstack=52;play.accept=function(card){return false;}
play.can_drag=function(index){return index==this.top_index();}
play.xoffset=30;play.yoffset=0;play.coords=function(_index,_pile_length){var index=parseInt(_index),pl=_pile_length||this.pile.length;var c=Math.max(0,cards-1-Math.max(0,this.top_index()-index));return{x:c*this.xoff(),y:c*this.yoff()};}}
function pile(div,game){if(arguments.length)this.init(div,game);}
pile.prototype.init=function(div,game,empty){this.game=game;this.name=div;this.pile=new Array(0);this.div=document.getElementById(div);if(empty||empty==undefined){this.empty=new card("empty-pile",undefined,undefined,up,null);this.empty.draw(this.div,{x:0,y:0},0);}
if(!this.div)throw("No <DIV> found for "+div);if(this.xoffset==undefined)this.xoffset=0;if(this.yoffset==undefined)this.yoffset=30;if(this.accept_stacks==undefined)this.accept_stacks=false;if(this.drag_stacks==undefined)this.drag_stacks=true;var _this=this;this.onmousedown=function(event,double_click){var e=new plat.event(event,_this.div);var drag={i:undefined,handle:{x:0,y:0},start:{x:e.pageX,y:e.pageY,time:(new Date()).getTime()}};if(_this.onclick)return _this.onclick(e);if(!_this.pile.length)return false;for(var i=_this.top_index();i>=0;i--){var c=_this.coords(i);if(e.targetX>c.x&&e.targetX<c.x+cardset.width&&e.targetY>c.y&&e.targetY<c.y+cardset.height){drag.i=i;break;}}
if(drag.i==undefined)return false;drag.state=game.get_state();drag.preliminaries=[];while(!_this.can_drag(drag.i)){var preliminary=undefined;for(var i=drag.i+1;i<_this.pile.length;i++){if(_this.can_drag(i)){var card=_this.pile.slice(i,_this.drag_stacks?_this.pile.length:i+1);var destinations=game.find_target_piles(card,_this);if(destinations.length){preliminary=_this._move_to_pile(i,card.length,destinations[0],null);break;}}}
if(!preliminary){game.set_state(drag.state,true);return false;}
drag.preliminaries.push(preliminary);}
drag.card=this.pile.slice(drag.i,_this.drag_stacks?_this.pile.length:drag.i+1);drag.destinations=[];{var state=game.get_state();for(var i in game.piles){var p=game.piles[i];if(p==this)
continue;var preliminaries=[];do{var affinity;if((affinity=p.target_affinity(drag.card)))
drag.destinations.push({to:p,tstart:p.pile.length,tpreliminaries:preliminaries.slice(),from:this,fstart:drag.i,n:drag.card.length,removed:drag.card,affinity:affinity});var preliminary=undefined;for(var i=0;i<p.pile.length;i++){if(p.can_drag(i)){var card=p.pile.slice(i,p.drag_stacks?p.pile.length:i+1);var destinations=game.find_target_piles(card,this,p);if(destinations.length){preliminary=p._move_to_pile(i,card.length,destinations[0],null);preliminaries.push(preliminary);break;}}}}while(preliminary);game.set_state(state,true);}}
drag.destinations.sort(function(a,b){return(!b.tpreliminaries.length-!a.tpreliminaries.length)||(b.affinity-a.affinity)});if(double_click&&!drag.destinations)return true;drag.handle.x=e.layerX;drag.handle.y=e.layerY;drag.old_move=document.onmousemove;drag.old_up=document.onmouseup;document.onmousemove=function(event){if(!drag.animate){for(var i in drag.preliminaries)
_this.animate_move(drag.preliminaries[i],null);drag.animate=true;}
var e=new plat.event(event);for(var i in drag.card){drag.card[i].draw(document.body,{x:e.pageX-drag.handle.x+_this.xoff()*i,y:e.pageY-drag.handle.y+_this.yoff()*i},100000+i);drag.card[i].dirty=true;}
var m=find_best_move(drag.destinations,e.pageX-drag.handle.x,e.pageY-drag.handle.y);if(!m||drag.destination!=m){if(drag.destination){for(var i=drag.destination.tpreliminaries.length-1;i>=0;i--)
_this.animate_move(_this.reverse_move(drag.destination.tpreliminaries[i]),null);drag.destination=undefined;}
drag.hover_timeout=cancel_timeout(drag.hover_timeout);}
if(m&&(drag.destination?drag.destination!=m:!drag.hover_timeout)){drag.hover_timeout=cancel_timeout(drag.hover_timeout);drag.hover_timeout=timeout(1000,function(){for(var i in m.tpreliminaries)
_this.animate_move(m.tpreliminaries[i],null);drag.hover_timeout=undefined;});drag.destination=m;}
if(m)
Highlight.draw(m.to.div,m.to.coords(m.tstart),m.tstart+0.5);else
Highlight.erase();return false;};document.onmouseup=function(event){var e=new plat.event(event);var m;if(!drag.animate||(new Date).getTime()-drag.start.time<250&&point_distance(drag.start,{x:e.pageX,y:e.pageY})<cardset_pixels(50))
double_click=true;if(!double_click){m=find_best_move(drag.destinations,e.pageX-drag.handle.x,e.pageY-drag.handle.y)}else
m=drag.destinations[0];if(drag.hover_timeout){drag.hover_timeout=cancel_timeout(drag.hover_timeout);drag.destination=undefined;}
Highlight.erase();if(m){game.push_state(drag.state);if(!drag.animate)
for(var i in drag.preliminaries)
_this.animate_move(drag.preliminaries[i],null);for(var i in m.tpreliminaries){_this.do_move(m.tpreliminaries[i]);if(drag.destination!=m)
_this.animate_move(m.tpreliminaries[i],null);}
_this.move_to_pile(drag.i,drag.card.length,m.to,null);}else{_this.animate_move([{from:_this,to:_this,tstart:drag.i,n:drag.card.length,removed:drag.card}],null);if(drag.state)game.set_state(drag.state,true);for(i=drag.preliminaries.length-1;i>=0;i--)
_this.animate_move(_this.reverse_move(drag.preliminaries[i]),null);}
document.onmousemove=drag.old_move;document.onmouseup=drag.old_up;return false;};if(double_click)
document.onmouseup(undefined);return false;}
this.div.onmousedown=function(event){return _this.onmousedown(event,false);}
game.piles.push(this);}
pile.prototype.toString=function(){return this.name+": "+this.pile;}
pile.prototype.update_stats=function(){this.game.stats.set(this.name,this.pile.length);this.game.stats.max(this.name+"_max",this.pile.length);this.game.stats.min(this.name+"_min",this.pile.length);}
pile.prototype.pop=function(){return this.splice(this.top_index(),1)[0];}
pile.prototype.push=function(card){card.dirty=true;var length=this.pile.push(card);this.update_stats();return length;}
pile.prototype.top=function(){if(!this.pile.length)alert("there is no top of an empty pile");return this.pile[this.top_index()];}
pile.prototype.top_index=function(){return this.pile.length-1;}
pile.prototype.xoff=function(){return this.xoffset*cardset.width/90;}
pile.prototype.yoff=function(){return this.yoffset*cardset.height/131;}
pile.prototype.coords=function(_index,_pile_length){var index=parseInt(_index),pl=_pile_length||this.pile.length;return{x:index*this.xoff(),y:index*this.yoff()};}
if(0){pile.prototype.coords=function(_index,_pile_length){var index=parseInt(_index),pl=_pile_length||this.pile.length;if(index<=0)return{x:0,y:0};var l=this.coords(index-1,pl);return{x:Math.max(0,l.x+Math.max(Math.min(6,this.xoff()),this.xoff()-2*(pl-index))),y:Math.max(0,l.y+Math.max(Math.min(6,this.yoff()),this.yoff()-2*(pl-index)))};}
pile.prototype.coords=function(_index,_pile_length){var index=parseInt(_index),pl=_pile_length||this.pile.length;var c={"x":(pl>0?Math.max(0,index*(this.xoff()+1)-((index+1)*index)/2-index*(pl-index-1)):index*this.xoff()),"y":(pl>0?Math.max(0,index*(this.yoff()+1)-((index+1)*index)/2-index*(pl-index-1)):index*this.yoff())};return c;}}
var Force=1,Resize=2;pile.prototype.update=function(style){this.game.stats.set(this.name,this.pile.length);for(var c in this.pile){if(style!=Resize&&(style==Force||this.pile[c].dirty||this.pile[c].dirty==undefined)){this.pile[c].draw(this.div,this.coords(c),c);this.pile[c].dirty=false;}
this.pile[c].image().style.cursor=this.can_drag(parseInt(c))?"move":"default";}
this.div.style.width=px(cardset.width+this.coords(Math.max(this.top_index(),(this.maxstack||1)-1),Math.max(this.top_index(),(this.maxstack||1)-1)).x);this.div.style.height=px(cardset.height+this.coords(Math.max(this.top_index(),(this.maxstack||1)-1),Math.max(this.top_index(),(this.maxstack||1)-1)).y);}
pile.prototype.splice=function(start,n){var removed=this.pile.splice(start,n);this.update_stats();return removed;};pile.prototype.splice_and_turn_over=function(start,n){var removed=pile.prototype.splice.call(this,start,n);if(this.pile.length){this.top().face=up;this.top().dirty=true;}
return removed;}
pile_game.prototype.replay=function(replay){var step=wrap(this,function(){if(replay==""){printf("Done\n");return;}
var m,from,start,n,to,rest;if(m=replay.match(/^\*(.)(.*)$/)){this.piles[base64.from(m[1])].onclick();replay=m[2];return step();}
else if(m=replay.match(/^(.)-(.)(.)(.)(.*)$/)){from=m[1];start=m[2];n=m[3];to=m[4];rest=m[5];}
else if(m=replay.match(/^(.):(.)(.)(.*)$/)){from=m[1];n=m[2];to=m[3];rest=m[4];}
else if(m=replay.match(/^(.)([^-:])(.*)$/)){from=m[1];to=m[2];rest=m[3];}
else printf("Unparsable: %s\n",replay);replay=rest;from=base64.from(from);to=base64.from(to);n=n!=undefined?base64.from(n):1;start=start!=undefined?base64.from(start):this.piles[from].pile.length-n;printf("Move: pile: %d (%s), start: %d, n: %d, to: %d (%s)\n",from,this.piles[from].name,start,n,to,this.piles[to].name);this.push_state();this.piles[from].move_to_pile(start,n,this.piles[to],step);});step();};pile_game.prototype.find_pile=function(pile){for(var p=0;p<this.piles.length;p++)
if(this.piles[p]==pile)
return base64.to(p);return"?";};pile_game.prototype.add_move=function(from,start,n,to)
{var m;if(start+n==from.pile.length)
if(n==1)m=sprintf("%s%s",this.find_pile(from),this.find_pile(to));else m=sprintf("%s:%s%s",this.find_pile(from),base64.to(n),this.find_pile(to));else m=sprintf("%s-%s%s%s",this.find_pile(from),base64.to(start),base64.to(n),this.find_pile(to));this.moves.push(m);Cookie.set("moves",this.seed+"#"+this.stats.set("moves",this.moves.join("")));}
pile_game.prototype.end=function(complete){Cookie.set("moves","");seed_game.prototype.end.call(this,complete);}
pile.prototype.reverse_move=function(move){return map(function(m){return{from:m.to,to:m.from,n:m.n,fstart:m.tstart,tstart:m.fstart,removed:m.removed}},move);}
pile.prototype.animate_move=function(m,callback){for(var t=0;t<m.length;t++){if(m[t].from!=m[t].to)
for(var i=m[t].fstart;i<m[t].from.pile.length;i++)
m[t].from.pile[i].dirty=true;for(var i=0;i<m[t].n;i++){m[t].removed[i].animate_to(m[t].to.div,m[t].to.coords(m[t].tstart+i),m[t].tstart+i,callback);callback=undefined;}
m[t].to.update(Force);m[t].from.update(Force);}}
pile.prototype.do_move=function(m){for(var t=0;t<m.length;t++){this.game.add_move(m[t].from,m[t].fstart,m[t].n,m[t].to);m[t].removed=m[t].from.splice(m[t].fstart,m[t].n);m[t].tstart=m[t].to.top_index()+1;for(var i in m[t].removed)
m[t].to.push(m[t].removed[i]);}
return m;}
pile.prototype._move_to_pile=function(start,n,to){var m=to.split_move?to.split_move(start,n,this).reverse():[{from:this,to:to,fstart:start,n:n}];this.do_move(m);return m;}
pile.prototype.move_to_pile=function(start,n,to,callback){this.animate_move(this._move_to_pile(start,n,to),callback);this.game.end_of_move();}
pile.prototype.accept=function(card){return 1;}
pile.prototype.can_drag=function(index){return true;}
pile.prototype.pageCoords=function(){return PageCoords(this.div);}
var gfis_next=[hearts,clubs,spades,diamonds],gfis=spades;function generic_foundation(div,game,draggable){if(arguments.length)this.init(div,game,draggable);}
generic_foundation.prototype=new pile();var gfis_next=[hearts,clubs,spades,diamonds],gfis=spades;generic_foundation.prototype.init=function(div,game,draggable){pile.prototype.init.call(this,div,game);this.accept=wrap(this,function(card){return this.pile.length==0&&card.rank==ace||this.pile.length&&in_suit_order(this.top(),card)?10+(card.suit==this.suit):0;});this.can_drag=function(index){return draggable&&index==this.top_index();}
this.xoffset=0;this.yoffset=0;this.icon=new card("F",this.suit=gfis=gfis_next[gfis],0,up,null);this.icon.draw(this.div,{x:0,y:0},0);}
pile_game.prototype.update=function(force){this.update_game_link(force);for(var i in this.piles)
this.piles[i].update(force);}
pile.prototype.target_affinity=function(card){if(this.accept_stacks)
return this.accept(card);else if(card.length==1)
return this.accept(card[0]);}
pile_game.prototype.find_target_piles=function(card,pile,other_pile){var p=[];for(var i in this.piles){var x={pile:this.piles[i],sort:this.piles[i].target_affinity(card)};if(x.pile!=pile&&x.pile!=other_pile&&x.sort)
p.push(x);}
p.sort(function(a,b){return b.sort-a.sort});for(var i in p)
p[i]=p[i].pile;return p;}
function find_best_move(inm,x,y){var m=[];for(var i in inm){var o=translate(inm[i].to.pageCoords(),inm[i].to.coords(inm[i].tstart));var dx=Math.abs(o.x-x),dy=Math.abs(o.y-y);if(dx<cardset.width&&dy<cardset.height)
m.push({area:dx*dy,move:inm[i]});}
m.sort(function(a,b){return a.area-b.area});if(m.length)return m[0].move;}
function find(array,card){for(var i in array)
if(array[i]==card)
return i;}
function drag_max_given_free(piles,dest)
{var drag_max=1;for(var i in piles)
if(piles[i].pile.length==0&&piles[i]!==dest)
drag_max*=2;return drag_max;}
function empty_pile_count(piles){var free=0;for(var i in piles)
if(piles[i].pile.length==0)
free++;return free;}
var the_game;on_load(function(){the_game=new game;the_game.new_game(URL.param.game,URL.param.replay);gotd.start=function(){the_game.new_game(game_of_the_day);return false;};});