5 function sp_init(cards, tmp0, tmp1)
8 cards ="As Ks Qs Js 10s 9s 8s 7s 6s 5s 4s 3s 2s "\
9 "Ah Kh Qh Jh 10h 9h 8h 7h 6h 5h 4h 3h 2h "\
10 "Ac Kc Qc Jc 10c 9c 8c 7c 6c 5c 4c 3c 2c "\
11 "Ad Kd Qd Jd 10d 9d 8d 7d 6d 5d 4d 3d 2d"
13 for (i=1; i<=length(tmp0); i++)
17 function sp_reset(type)
21 sp_from = "" # The speakers player name
22 sp_valid = "" # It is the speaker turn
27 sp_suit = "" # The lead suit {s,h,d,c}
28 sp_piles = "" # [x] Played cards this turn
29 delete sp_pile # [x] Played cards this turn
34 sp_state = "bid" # {new,join,bid,pass,play}
35 sp_broken = 0 # Whether spades are broken
36 delete sp_last # [x] The result of the last hand
37 delete sp_hands # [p] Each players cards
38 delete sp_looked # [i] Whether a player has looked a their cards
39 delete sp_bids # [i] Each players bid
40 delete sp_nil # [i] Nil multiplier 0=regular, 1=nil, 2=blind
41 delete sp_pass # [i] Cards to pass
42 delete sp_tricks # [i] Tricks this round
47 sp_state = "new" # {new,join,bid,pass,play}
48 sp_owner = "" # Who started the game
49 sp_playto = 0 # Score the game will go to
50 sp_dealer =-1 # Who is dealing this round
51 sp_turn = 0 # Index of who's turn it is
52 sp_player = "" # Who's turn it is
53 sp_limit = 10 # Bag out limit / nil bonus
54 delete sp_players # [p] Player names players["name"] -> i
55 delete sp_auths # [c] Player auth names auths["auth"] -> "name"
56 delete sp_share # [c] Player teammates share["friend"] -> "name"
57 delete sp_order # [i] Player order order[i] -> "name"
58 delete sp_scores # [i] Teams score
63 sp_channel = "" # channel to play in
64 sp_log = "" # Log file name
65 delete sp_notify # [p] E-mail notification address
69 function sp_acopy(dst, src, key)
74 json_copy(dst, key, src[key])
78 function sp_save(file, game)
81 game["suit"] = sp_suit;
82 game["piles"] = sp_piles;
83 json_copy(game, "pile", sp_pile);
86 game["state"] = sp_state;
87 game["broken"] = sp_broken;
88 json_copy(game, "last", sp_last);
89 json_copy(game, "looked", sp_looked);
90 json_copy(game, "bids", sp_bids);
91 json_copy(game, "nil", sp_nil);
92 json_copy(game, "pass", sp_pass);
93 json_copy(game, "tricks", sp_tricks);
96 game["owner"] = sp_owner;
97 game["playto"] = sp_playto;
98 game["dealer"] = sp_dealer;
99 game["turn"] = sp_turn;
100 game["player"] = sp_player;
101 game["limit"] = sp_limit;
102 json_copy(game, "hands", sp_hands);
103 json_copy(game, "players", sp_players);
104 json_copy(game, "auths", sp_auths);
105 json_copy(game, "share", sp_share);
106 json_copy(game, "order", sp_order);
107 json_copy(game, "scores", sp_scores);
110 game["channel"] = sp_channel;
111 game["log"] = sp_log;
112 json_copy(game, "notify", sp_notify);
115 json_save(file, game);
118 function sp_load(file, game)
121 if (!json_load(file, game))
125 sp_suit = game["suit"];
126 sp_piles = game["piles"];
127 sp_acopy(sp_pile, game["pile"]);
130 sp_state = game["state"];
131 sp_broken = game["broken"];
132 sp_acopy(sp_last, game["last"]);
133 sp_acopy(sp_looked, game["looked"]);
134 sp_acopy(sp_bids, game["bids"]);
135 sp_acopy(sp_nil, game["nil"]);
136 sp_acopy(sp_pass, game["pass"]);
137 sp_acopy(sp_tricks, game["tricks"]);
140 sp_owner = game["owner"];
141 sp_playto = game["playto"];
142 sp_dealer = game["dealer"];
143 sp_turn = game["turn"];
144 sp_player = game["player"];
145 sp_limit = game["limit"];
146 sp_acopy(sp_hands, game["hands"]);
147 sp_acopy(sp_players, game["players"]);
148 sp_acopy(sp_auths, game["auths"]);
149 sp_acopy(sp_share, game["share"]);
150 sp_acopy(sp_order, game["order"]);
151 sp_acopy(sp_scores, game["scores"]);
154 sp_channel = game["channel"];
155 sp_log = game["log"];
156 sp_acopy(sp_notify, game["notify"]);
161 print strftime("%Y-%m-%d %H:%M:%S | ") msg >> "logs/" sp_log
162 fflush("logs/" sp_log)
163 say(sp_channel, msg);
166 function sp_pretty(cards, who)
169 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
170 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
172 if (!nounicode[who]) {
173 gsub(/s/, "\002♠", cards)
174 gsub(/h/, "\002♥", cards)
175 gsub(/d/, "\002♦", cards)
176 gsub(/c/, "\002♣", cards)
181 function sp_next(who, prev)
184 sp_turn = who ? sp_players[who] : (sp_turn + 1) % 4
185 if (length(sp_order) == 4)
186 sp_player = sp_order[sp_turn]
190 function sp_shuf(i, mixed)
192 sp_usort(sp_players, mixed)
194 sp_order[i-1] = mixed[i]
195 sp_players[mixed[i]] = i-1
199 function sp_deal( shuf)
201 sp_say("/me deals the cards")
202 sp_usort(sp_deck, shuf)
203 for (i=1; i<=52; i++)
204 sp_hands[sp_order[i%4]][shuf[i]] = 1
206 sp_dealer = (sp_dealer+1)%4
208 sp_player = sp_order[sp_turn]
209 sp_say(sp_player ": you bid first!")
212 function sp_hand(to, who, sort, str)
214 asorti(sp_hands[who], sort, "sp_csort")
215 for (i=0; i<length(sort); i++)
216 str = str "" sprintf("%4s", sort[i])
217 gsub(/^ +| +$/, "", str)
218 return sp_pretty(str, to)
221 function sp_hasa(who, expr)
223 for (c in sp_hands[who]) {
230 function sp_type(card)
232 return substr(card, length(card))
235 function sp_usort(list, out) {
238 asorti(out, out, "@val_num_asc")
241 function sp_csort(i1,v1,i2,v2) {
242 return sp_deck[i1] > sp_deck[i2] ? +1 :
243 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
246 function sp_winner( card, tmp)
248 for (card in sp_pile)
249 if (card !~ sp_suit && card !~ /s/)
251 asorti(sp_pile, tmp, "sp_csort")
252 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
258 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
259 return sp_order[i+0] "/" sp_order[i+2]
262 function sp_bags(i, bags)
264 bags = sp_scores[i] % sp_limit
272 return sp_nil[who] == 0 ? sp_bids[who] :
273 sp_nil[who] == 1 ? "nil" :
274 sp_nil[who] == 2 ? "blind" : "n/a"
277 function sp_passer(who)
279 return sp_nil[(who+0)%4] == 2 || sp_nil[(who+1)%4] != 0 ||
280 sp_nil[(who+2)%4] == 2 || sp_nil[(who+3)%4] != 0
283 function sp_bidders( i, turn, bid, bids)
285 for (i = 0; i < 4; i++) {
286 turn = (sp_dealer + i) % 4
287 if (bid = sp_bid(turn))
288 bids = bids " " sp_order[turn] ":" bid
290 gsub(/^ +| +$/, "", bids)
294 function sp_score( bids, times, tricks)
296 for (i=0; i<2; i++) {
297 bids = sp_bids[i] + sp_bids[i+2]
298 tricks = sp_tricks[i] + sp_tricks[i+2]
300 times = int((sp_bags(i) + bags) / sp_limit)
302 sp_say(sp_team(i) " bag" (times>1?" way ":" ") "out")
303 sp_scores[i] -= sp_limit * 10 * times;
305 if (tricks >= bids) {
306 sp_say(sp_team(i) " make their bid: " tricks "/" bids)
307 sp_scores[i] += bids*10 + bags;
309 sp_say(sp_team(i) " go bust: " tricks "/" bids)
310 sp_scores[i] -= bids*10;
313 for (i=0; i<4; i++) {
316 sp_say(sp_order[i] " " \
317 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
318 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
319 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
320 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
322 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
323 (sp_tricks[i] == 0 ? 1 : -1)
325 if (sp_scores[0] > sp_scores[1])
326 sp_say(sp_team(0) " lead " sp_scores[0] " to " sp_scores[1] " of " sp_playto)
327 else if (sp_scores[1] > sp_scores[0])
328 sp_say(sp_team(1) " lead " sp_scores[1] " to " sp_scores[0] " of " sp_playto)
330 sp_say("tied at " sp_scores[0] " of " sp_playto)
333 function sp_play(card, winner, pi)
335 delete sp_hands[sp_from][card]
336 sp_pile[card] = sp_player
337 sp_piles = sp_piles (sp_piles?",":"") card
344 if (length(sp_pile) == 1)
345 sp_suit = sp_type(card)
348 if (length(sp_pile) == 4) {
350 pi = sp_players[sp_pile[winner]]
352 sp_say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
353 " (" sp_pretty(sp_piles, FROM) ")")
354 sp_last["player"] = sp_pile[winner];
355 sp_last["pile"] = sp_piles;
356 sp_next(sp_pile[winner])
361 if (sp_tricks[0] + sp_tricks[1] + \
362 sp_tricks[2] + sp_tricks[3] == 13) {
363 sp_say("Round over!")
365 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
366 sp_scores[0] != sp_scores[1]) {
368 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
370 say(CHANNEL, sp_team(winner) " wins the game " \
371 sp_scores[winner] " to " sp_scores[looser])
372 say(CHANNEL, sp_order[winner+0] "++")
373 say(CHANNEL, sp_order[winner+2] "++")
377 if (sp_scores[0] == sp_scores[1] &&
378 sp_scores[0] >= sp_playto)
379 sp_say("It's tie! Playing an extra round!");
387 function sp_delay(sec)
389 return (sec > 60*60*24 ? int(sec/60/60/24) "d " : "") \
390 (sec > 60*60 ? int(sec/60/60)%24 "h " : "") \
394 function sp_max(list, i, max)
396 for (i=0; i<length(list); i++)
397 if (max == "" || list[i] > max)
402 function sp_avg(list, i, sum)
404 for (i=0; i<length(list); i++)
406 return sum / length(list)
409 function sp_stats(file, line, arr, time, user, turn, start, delay)
412 while ((stat = getline line < file) > 0) {
414 if (!match(line, /^([0-9\- \:]*) \| (.*)$/, arr))
416 gsub(/[:-]/, " ", arr[1])
417 time = mktime(arr[1])
420 if (!match(arr[2], /^([^:]*): (.*)$/, arr))
424 # Record user latency
426 delay[turn][length(delay[turn])] = time - start
429 if (match(arr[2], /^it is your.*$/, arr)) {
438 reply("File does not exist: " file);
441 for (user in delay) {
442 say("latency for " user \
443 ": " sp_delay(sp_avg(delay[user])) " (avg)" \
444 ", " sp_delay(sp_max(delay[user])) " (max)")
450 cmd = "od -An -N4 -td4 /dev/random"
456 sp_load("var/sp_cur.json");
458 # sp_say("Game restored.")
462 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
463 AUTH in sp_share ? sp_share[AUTH] : FROM
464 sp_valid = sp_from && sp_from == sp_player
470 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
475 sp_save("var/sp_save.json");
481 sp_load("var/sp_save.json");
487 say(".help spades -- play a game of spades")
490 /^\.help [Ss]pades$/ {
491 say("Spades -- play a game of spades")
492 say(".help game -- setup and administer the game")
493 say(".help play -- commands for playing spades")
494 say(".help auth -- control player authorization")
499 say(".newgame [score] -- start a game to <score> points, default 500")
500 say(".endgame -- abort the current game")
501 say(".savegame -- save the current game to disk")
502 say(".loadgame -- load the previously saved game")
507 say(".join -- join the current game")
508 say(".look -- look at your cards")
509 say(".bid [n] -- bid for <n> tricks")
510 say(".pass [card] -- pass a card to your partner")
511 say(".play [card] -- play a card")
512 say(".last -- show who took the previous trick")
513 say(".turn -- check whose turn it is")
514 say(".bids -- check what everyone bid")
515 say(".tricks -- check how many trick have been taken")
516 say(".score -- check the score")
521 say(".auth [who] -- display authentication info for a user")
522 say(".allow [who] -- allow another person to play on your behalf")
523 say(".deny [who] -- prevent a previously allowed user from playing")
524 say(".show -- display which users can play for which players")
525 say(".notify [addr] -- email user when it is their turn")
531 /^\.deal (\w+) (.*)/ {
532 sp_say(FROM " is cheating for " $2)
534 for (i=3; i<=NF; i++)
540 /^\.order (\w+) ([0-4])/ {
541 sp_say(FROM " is cheating for " $2)
544 sp_player = sp_order[sp_turn]
548 sp_state == "play" &&
549 /^\.force (\w+) (\S+)$/ {
550 sp_say(FROM " is cheating for " $2)
558 match($0, /^\.newgame ?([1-9][0-9]*) *- *([1-9][0-9]*)$/, _arr) {
559 if (_arr[2] > _arr[1])
560 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
563 /^\.newgame ?([1-9][0-9]*)?$/ {
564 if (sp_state != "new") {
565 reply("There is already a game in progress.")
569 sp_playto = $2 ? $2 : 200
570 sp_limit = sp_playto > 200 ? 10 : 5;
573 sp_log = strftime("%Y%m%d_%H%M%S.log")
574 sp_say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
578 (sp_from == sp_owner || AUTH == OWNER) &&
580 if (sp_state == "new") {
581 reply("There is no game in progress.")
583 sp_say(FROM " ends the game")
589 if (sp_state == "new") {
590 reply("There is no game in progress")
592 else if (sp_state == "play") {
593 reply("The game has already started")
595 else if (sp_state == "join" && sp_from in sp_players) {
596 reply("You are already playing")
598 else if (sp_state == "join") {
602 sp_auths[AUTH] = FROM
604 sp_say(FROM " joins the game!")
606 if (sp_state == "join" && sp_turn == 0) {
613 _who = $2 in USERS ? USERS[$2]["auth"] : ""
614 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
615 if (sp_state ~ "new|join") {
616 reply("The game has not yet started")
618 else if (!(sp_from in sp_players)) {
619 reply("You are not playing")
622 reply(_str " is not logged in")
624 else if (_who in sp_players || _who in sp_auths) {
625 reply(_str " is a primary player")
627 else if (_who in sp_share) {
628 reply(_str " is already playing for " sp_share[_who])
631 sp_say(_str " can now play for " sp_from)
632 sp_share[_who] = sp_from
637 _who = $2 in USERS ? USERS[$2]["auth"] : $2
638 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
639 if (sp_state ~ "new|join") {
640 reply("The game has not yet started")
642 else if (!(sp_from in sp_players)) {
643 reply("You are not playing")
645 else if (_who in sp_players || _who in sp_auths) {
646 reply(_str " is a primary player")
648 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
649 reply(_str " is not playing for " sp_from)
652 sp_say(_str " can no longer play for " sp_from)
653 delete sp_share[_who]
658 if (sp_from in sp_notify)
659 reply("Your address is " sp_notify[sp_from])
661 reply("Your address is not set")
665 if (sp_from in sp_notify) {
666 reply("Removing address " sp_notify[sp_from])
667 delete sp_notify[sp_from]
669 reply("Your address is not set")
673 /^\.notify \S+@\S+.\S+$/ {
675 gsub(/[^a-zA-Z0-9_+@.-]/, "", _addr)
676 sp_notify[sp_from] = _addr
677 reply("Notifying you at " _addr)
680 sp_state ~ "(bid|pass|play)" &&
684 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
686 sp_say(_i " allowed:" _lines[_i])
690 (sp_state == "bid" || sp_state == "play") &&
692 if (sp_from in sp_players)
693 reply("It is not your turn.")
695 reply("You are not playing.")
700 /^\.bid (0|[1-9][0-9]*)$/ {
701 if ($2 < 0 || $2 > 13) {
702 reply("You can only bid from 0 to 13")
706 if ($2 == 0 && !sp_looked[i]) {
707 sp_say(FROM " goes blind nil!")
709 } else if ($2 == 0) {
710 sp_say(FROM " goes nil!")
715 if (sp_turn != sp_dealer) {
716 sp_say(sp_player ": it is your bid! (" sp_bidders() ")")
718 for (p in sp_players)
719 say(p, "You have: " sp_hand(p, p))
721 for (i=0; i<2; i++) {
723 sp_say(sp_team(i) ": select a card to pass " \
724 "(/msg " NICK " .pass <card>)")
728 if (sp_state == "play")
729 sp_say(sp_player ": you have the opening lead!")
734 sp_state == "pass" &&
737 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
739 # check validity and pass
740 if (!(sp_from in sp_players)) {
741 reply("You are not playing.")
743 else if (!sp_passer(_team)) {
744 reply("Your team did not go blind")
746 else if (sp_pass[sp_players[sp_from]]) {
747 reply("You have already passed a card")
749 else if (!(_card in sp_deck)) {
750 reply("Invalid card")
752 else if (!(_card in sp_hands[sp_from])) {
753 reply("You do not have that card")
756 sp_pass[sp_players[sp_from]] = $2
757 sp_say(FROM " passes a card")
760 # check for end of passing
761 if ((!sp_passer(0) || (sp_pass[0] && sp_pass[2])) &&
762 (!sp_passer(1) || (sp_pass[1] && sp_pass[3]))) {
766 delete sp_hands[sp_order[i]][_card]
767 sp_hands[sp_order[_partner]][_card] = 1
769 sp_say("Cards have been passed!")
770 sp_say(sp_player ": you have the opening lead!")
771 for (p in sp_players)
772 say(p, "You have: " sp_hand(p, p))
777 sp_state ~ "(bid|pass|play)" &&
779 if (!(sp_from in sp_players)) {
780 reply("You are not playing.")
782 sp_looked[sp_players[sp_from]] = 1
783 say(FROM, "You have: " sp_hand(FROM, sp_from))
788 sp_state == "play" &&
791 gsub(/[^A-Za-z0-9]/, "", _card);
792 if (!(_card in sp_deck)) {
793 reply("Invalid card")
795 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
796 reply("You must follow suit (" sp_suit ")")
798 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
799 reply("You cannot trump on the first hand")
801 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
802 reply("Spades have not been broken")
804 else if (!(_card in sp_hands[sp_from])) {
805 reply("You do not have that card")
809 if (sp_state == "play") {
810 if (length(sp_hands[sp_from]))
811 say(FROM, "You have: " sp_hand(FROM, sp_from))
813 sp_say(sp_player ": it is your turn! " \
814 "(" sp_pretty(sp_piles, sp_player) ")")
816 sp_say(sp_player ": it is your turn!")
821 /^\.last/ && sp_state == "play" {
822 if (!isarray(sp_last))
823 say("No tricks have been taken!");
825 say(sp_last["player"] " took " \
826 sp_pretty(sp_last["pile"], FROM));
829 /^\.bids/ && sp_state == "bid" ||
830 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
832 _pile = sp_pretty(sp_piles, FROM)
836 if (/!/ && sp_share[_i] == sp_player)
837 _extra = _extra " " _i "!"
839 if (sp_state == "bid" && !_bids)
840 say("It is " sp_player "'s bid!" _extra)
841 if (sp_state == "bid" && _bids)
842 say("It is " sp_player "'s bid!" _extra " (" _bids ")")
843 if (sp_state == "play" && !_pile)
844 say("It is " sp_player "'s turn!" _extra)
845 if (sp_state == "play" && _pile)
846 say("It is " sp_player "'s turn!" _extra " (" _pile ")")
848 for (_i=0; sp_state == "pass" && _i<4; _i++)
849 if (sp_passer(_i) && !sp_pass[_i])
850 say("Waiting for " sp_order[_i] " to pass a card!")
852 if (/!!/ && (sp_state == "bid" || sp_state == "play")) {
853 if (sp_player in sp_notify) {
854 _bids = _bids ? _bids : "none"
855 _pile = _pile ? sp_piles : "none"
856 mail_send(sp_notify[sp_player], \
857 "It is your " sp_state "!", \
858 "Bids so far: " _bids "\n" \
859 "Cards played: " _pile)
860 say("Notified " sp_player " at " sp_notify[sp_player])
862 say("No email address for " sp_player)
867 /^\.bids$/ && sp_state ~ "(pass|play)" {
868 say(sp_order[0] " bid " sp_bid(0) ", " \
869 sp_order[2] " bid " sp_bid(2) ", " \
870 "total: " sp_bids[0] + sp_bids[2])
871 say(sp_order[1] " bid " sp_bid(1) ", " \
872 sp_order[3] " bid " sp_bid(3) ", " \
873 "total: " sp_bids[1] + sp_bids[3])
876 /^\.tricks$/ && sp_state == "play" {
877 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
878 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
879 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
880 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
883 (TO == NICK || DST == sp_channel) &&
884 /^\.(score|status)$/ {
885 if (sp_state == "new") {
886 say("There is no game in progress")
888 if (sp_state ~ "join|bid|pass|play") {
890 sp_playto " points, " \
893 if (sp_state == "join") {
894 say("Waiting for players: " \
895 sp_order[0] " " sp_order[1] " " \
896 sp_order[2] " " sp_order[3])
898 if (sp_state ~ "bid|pass|play") {
899 say(sp_team(0) ": " \
900 int(sp_scores[0]) " points, " \
901 int(sp_bags(0)) " bags")
902 say(sp_team(1) ": " \
903 int(sp_scores[1]) " points, " \
904 int(sp_bags(1)) " bags")
908 (TO == NICK || DST == sp_channel) &&
910 say("http://pileus.org/andy/spades/" sp_log)
913 (TO == NICK || DST == sp_channel) &&
915 sp_stats("logs/" sp_log);
918 (TO == NICK || DST == sp_channel) &&
919 /^\.stats ([0-9]+_[0-9]+)(\.log)$/ {
920 gsub(/\.log$/, "", $2);
921 sp_stats("logs/" $2 ".log");
924 /^\.((new|end|load)game|join|look|bid|pass|play|notify)/ {
925 sp_save("var/sp_cur.json");