1 // This file is part of Freecell Solver. It is subject to the license terms in
2 // the COPYING.txt file found in the top-level directory of this distribution
3 // and at http://fc-solve.shlomifish.org/docs/distro/COPYING.html . No part of
4 // Freecell Solver, including this file, may be copied, modified, propagated,
5 // or distributed except according to the terms contained in the COPYING file.
6 //
7 // Copyright (c) 2011 Shlomi Fish
8 // delta_states_debondt_impl.h - "delta states" are an encoding of states,
9 // where the states are encoded and decoded based on a compact delta from the
10 // initial state.
11 //
12 // This encoding improves upon the original delta_states.c .
13 #include "indirect_buffer.h"
14 #include "delta_states_iface.h"
15 #include "delta_states.h"
16 
17 #ifdef FCS_DEBONDT_DELTA_STATES
18 #define FOUNDATION_BASE (RANK_KING + 1)
19 
20 enum
21 {
22     OPT_TOPMOST = 0,
23     OPT_DONT_CARE = OPT_TOPMOST,
24     OPT_FREECELL = 1,
25     OPT_ORIG_POS = 2,
26     NUM_KING_OPTS = 3,
27     OPT_PARENT_SUIT_MOD_IS_0 = 3,
28     OPT_PARENT_SUIT_MOD_IS_1 = 4,
29     NUM_OPTS = 5,
30     OPT_IN_FOUNDATION = 5,
31     OPT__BAKERS_DOZEN__ORIG_POS = 0,
32     OPT__BAKERS_DOZEN__FIRST_PARENT = 1,
33     NUM__BAKERS_DOZEN__OPTS = OPT__BAKERS_DOZEN__FIRST_PARENT + 4,
34     OPT__BAKERS_DOZEN__IN_FOUNDATION = NUM__BAKERS_DOZEN__OPTS,
35 };
36 
37 #define IS_BAKERS_DOZEN() (local_variant == FCS_DBM_VARIANT_BAKERS_DOZEN)
38 
fc_solve_delta_stater_init(fcs_delta_stater * const self,const fcs_dbm_variant_type local_variant,fcs_state * const init_state,const size_t num_columns,const int num_freecells PASS_ON_NOT_FC_ONLY (const int sequences_are_built_by))39 static inline void fc_solve_delta_stater_init(fcs_delta_stater *const self,
40     const fcs_dbm_variant_type local_variant, fcs_state *const init_state,
41     const size_t num_columns,
42     const int num_freecells PASS_ON_NOT_FC_ONLY(
43         const int sequences_are_built_by))
44 {
45     FCS_ON_NOT_FC_ONLY(self->sequences_are_built_by = sequences_are_built_by);
46     self->num_columns = num_columns;
47     self->num_freecells = num_freecells;
48     self->init_state = init_state;
49 
50     memset(self->bakers_dozen_topmost_cards_lookup, '\0',
51         sizeof(self->bakers_dozen_topmost_cards_lookup));
52 
53     fc_solve_var_base_writer_init(&self->w);
54     fc_solve_var_base_reader_init(&self->r);
55 
56     if (IS_BAKERS_DOZEN())
57     {
58 #pragma GCC diagnostic push
59 #ifndef __clang__
60 #pragma GCC diagnostic ignored "-Waggressive-loop-optimizations"
61 #endif
62         for (size_t col_idx = 0; col_idx < self->num_columns; ++col_idx)
63         {
64             const_AUTO(col, fcs_state_get_col(*init_state, col_idx));
65             const int col_len = fcs_col_len(col);
66 
67             if (col_len > 0)
68             {
69                 const_AUTO(top_card, fcs_col_get_card(col, 0));
70                 self->bakers_dozen_topmost_cards_lookup[top_card >> 3] |=
71                     (1 << (top_card & (8 - 1)));
72             }
73         }
74 #pragma GCC diagnostic pop
75     }
76 }
77 
fc_solve_delta_stater__init_card_states(fcs_delta_stater * const self)78 static inline void fc_solve_delta_stater__init_card_states(
79     fcs_delta_stater *const self)
80 {
81     int *const card_states = self->card_states;
82     for (size_t i = 0; i < COUNT(self->card_states); ++i)
83     {
84         card_states[i] = -1;
85     }
86 }
87 
88 #ifdef FCS_USE_INT128_FOR_VAR_BASE
89 #define fc_solve_delta_stater_release(s)
90 #else
fc_solve_delta_stater_release(fcs_delta_stater * const self)91 static inline void fc_solve_delta_stater_release(fcs_delta_stater *const self)
92 {
93     fc_solve_var_base_reader_release(&(self->r));
94     fc_solve_var_base_writer_release(&(self->w));
95 }
96 #endif
97 
wanted_suit_bit_opt(const fcs_card parent_card)98 static inline int wanted_suit_bit_opt(const fcs_card parent_card)
99 {
100     return ((fcs_card_suit(parent_card) & 0x2) ? OPT_PARENT_SUIT_MOD_IS_1
101                                                : OPT_PARENT_SUIT_MOD_IS_0);
102 }
103 
wanted_suit_idx_opt(const fcs_card parent_card)104 static inline int wanted_suit_idx_opt(const fcs_card parent_card)
105 {
106     return OPT__BAKERS_DOZEN__FIRST_PARENT + fcs_card_suit(parent_card);
107 }
108 
calc_child_card_option(const fcs_dbm_variant_type local_variant,const fcs_card parent_card,const fcs_card child_card PASS_ON_NOT_FC_ONLY (const int sequences_are_built_by))109 static inline int calc_child_card_option(
110     const fcs_dbm_variant_type local_variant, const fcs_card parent_card,
111     const fcs_card child_card PASS_ON_NOT_FC_ONLY(
112         const int sequences_are_built_by))
113 {
114     if (IS_BAKERS_DOZEN())
115     {
116         if ((fcs_card_rank(child_card) != 1) &&
117             (fcs_card_rank(child_card) + 1 == fcs_card_rank(parent_card)))
118         {
119             return wanted_suit_idx_opt(parent_card);
120         }
121         else
122         {
123             return OPT__BAKERS_DOZEN__ORIG_POS;
124         }
125     }
126     else
127     {
128         if ((fcs_card_rank(child_card) != 1) &&
129             (fcs_is_parent_card(child_card, parent_card)))
130         {
131             return wanted_suit_bit_opt(parent_card);
132         }
133         else
134         {
135             return OPT_ORIG_POS;
136         }
137     }
138 }
139 
get_top_rank_for_iter(const fcs_dbm_variant_type local_variant)140 static inline int get_top_rank_for_iter(
141     const fcs_dbm_variant_type local_variant)
142 {
143     return (IS_BAKERS_DOZEN() ? (RANK_KING - 1) : RANK_KING);
144 }
145 
fc_solve_delta_stater_encode_composite(fcs_delta_stater * const self,const fcs_dbm_variant_type local_variant,fcs_var_base_writer * const writer)146 static void fc_solve_delta_stater_encode_composite(fcs_delta_stater *const self,
147     const fcs_dbm_variant_type local_variant, fcs_var_base_writer *const writer)
148 {
149     fcs_state *const derived = self->derived_state;
150 
151     fc_solve_delta_stater__init_card_states(self);
152 
153     for (size_t suit_idx = 0; suit_idx < FCS_NUM_SUITS; ++suit_idx)
154     {
155         const unsigned long rank = fcs_foundation_value(*derived, suit_idx);
156 
157         fc_solve_var_base_writer_write(writer, FOUNDATION_BASE, rank);
158 
159         const unsigned long max_rank = ((rank < 1) ? 1 : rank);
160 
161         for (unsigned long r = 1; r <= max_rank; ++r)
162         {
163 #define CARD_POS(card) ((size_t)(card))
164 #define CARD_STATE(card) self->card_states[CARD_POS(card)]
165 #define SET_CARD_STATE(card, opt) CARD_STATE(card) = (opt)
166 #define RS_STATE(rank, suit_idx) CARD_STATE(fcs_make_card(rank, suit_idx))
167             RS_STATE(((fcs_card)r), ((fcs_card)suit_idx)) = OPT_DONT_CARE;
168         }
169     }
170 
171 #if MAX_NUM_FREECELLS > 0
172     for (int fc_idx = 0; fc_idx < self->num_freecells; ++fc_idx)
173     {
174         const fcs_card card = fcs_freecell_card(*derived, fc_idx);
175 
176         if (fcs_card_is_valid(card))
177         {
178             SET_CARD_STATE(card, OPT_FREECELL);
179         }
180     }
181 #endif
182 
183     if (IS_BAKERS_DOZEN())
184     {
185         for (size_t col_idx = 0; col_idx < self->num_columns; ++col_idx)
186         {
187             const_AUTO(col, fcs_state_get_col(*derived, col_idx));
188             const int col_len = fcs_col_len(col);
189 
190             if (!col_len)
191             {
192                 continue;
193             }
194             const_AUTO(top_card, fcs_col_get_card(col, 0));
195 
196             // Skip Aces which were already set.
197             if (fcs_card_rank(top_card) != 1)
198             {
199                 SET_CARD_STATE(top_card, OPT__BAKERS_DOZEN__ORIG_POS);
200             }
201 
202             for (int pos = 1; pos < col_len; ++pos)
203             {
204                 const fcs_card parent_card = fcs_col_get_card(col, pos - 1);
205                 const fcs_card this_card = fcs_col_get_card(col, pos);
206 
207                 // Skip Aces which were already set.
208                 if (fcs_card_rank(this_card) != 1)
209                 {
210                     SET_CARD_STATE(
211                         this_card, ((fcs_card_rank(this_card) + 1 ==
212                                         fcs_card_rank(parent_card))
213                                            ? wanted_suit_idx_opt(parent_card)
214                                            : OPT__BAKERS_DOZEN__ORIG_POS));
215                 }
216             }
217         }
218     }
219     else
220     {
221         for (size_t col_idx = 0; col_idx < self->num_columns; ++col_idx)
222         {
223             const_AUTO(col, fcs_state_get_col(*derived, col_idx));
224             const int col_len = fcs_col_len(col);
225 
226             if (!col_len)
227             {
228                 continue;
229             }
230             const_AUTO(top_card, fcs_col_get_card(col, 0));
231             if (fcs_card_rank(top_card) != 1)
232             {
233                 SET_CARD_STATE(top_card, OPT_TOPMOST);
234             }
235 
236             fcs_card parent_card = top_card;
237             for (int child_idx = 1; child_idx < col_len; ++child_idx)
238             {
239                 const fcs_card child_card = fcs_col_get_card(col, child_idx);
240 
241                 if (fcs_card_rank(child_card) != 1)
242                 {
243                     const int opt =
244                         calc_child_card_option(local_variant, parent_card,
245                             child_card PASS_ON_NOT_FC_ONLY(
246                                 self->sequences_are_built_by));
247                     SET_CARD_STATE(child_card, opt);
248                 }
249 
250                 parent_card = child_card;
251             }
252         }
253     }
254 
255     // All cards should be determined now - let's encode.
256     //
257     // The foundations have already been encoded.
258     //
259     // Skip encoding the aces, and the kings are encoded with less bits.
260     const int top_rank_for_iter = get_top_rank_for_iter(local_variant);
261     for (int rank = 2; rank <= top_rank_for_iter; ++rank)
262     {
263         for (int suit_idx = 0; suit_idx < FCS_NUM_SUITS; ++suit_idx)
264         {
265             const unsigned long opt =
266                 (unsigned long)RS_STATE((fcs_card)rank, (fcs_card)suit_idx);
267             unsigned long base;
268 
269             if (IS_BAKERS_DOZEN())
270             {
271                 const_AUTO(card, fcs_card2char(fcs_make_card(
272                                      (fcs_card)rank, (fcs_card)suit_idx)));
273 
274                 if (self->bakers_dozen_topmost_cards_lookup[card >> 3] &
275                     (1 << (card & (8 - 1))))
276                 {
277                     continue;
278                 }
279                 base = NUM__BAKERS_DOZEN__OPTS;
280             }
281             else
282             {
283                 base = ((rank == RANK_KING) ? NUM_KING_OPTS : NUM_OPTS);
284             }
285 
286             assert(opt < base);
287 
288             fc_solve_var_base_writer_write(writer, base, opt);
289         }
290     }
291 }
292 
delta_stater__fill_column_with_descendent_cards(fcs_delta_stater * const self,const fcs_dbm_variant_type local_variant,fcs_cards_column * const col)293 static inline void delta_stater__fill_column_with_descendent_cards(
294     fcs_delta_stater *const self, const fcs_dbm_variant_type local_variant,
295     fcs_cards_column *const col)
296 {
297     fcs_card parent_card = fcs_col_get_card(*col, fcs_col_len(*col) - 1);
298 
299     while (fcs_card_is_valid(parent_card))
300     {
301         const int wanted_opt =
302             (IS_BAKERS_DOZEN() ? wanted_suit_idx_opt(parent_card)
303                                : wanted_suit_bit_opt(parent_card));
304 
305         fcs_card child_card = fc_solve_empty_card;
306         const int candidate_rank = fcs_card_rank(parent_card) - 1;
307         for (int suit = (IS_BAKERS_DOZEN()
308                              ? 0
309                              : ((fcs_card_suit(parent_card) & (0x1)) ^ 0x1));
310              suit < FCS_NUM_SUITS; suit += (IS_BAKERS_DOZEN() ? 1 : 2))
311         {
312             const fcs_card candidate_card =
313                 fcs_make_card((fcs_card)candidate_rank, (fcs_card)suit);
314 
315             if (CARD_STATE(candidate_card) == wanted_opt)
316             {
317                 child_card = candidate_card;
318                 break;
319             }
320         }
321 
322         if (fcs_card_is_valid(child_card))
323         {
324             fcs_col_push_card(*col, child_card);
325         }
326         parent_card = child_card;
327     }
328 }
329 
fc_solve_delta_stater_decode(fcs_delta_stater * const self,const fcs_dbm_variant_type local_variant,fcs_var_base_reader * const reader,fcs_state * const ret)330 static void fc_solve_delta_stater_decode(fcs_delta_stater *const self,
331     const fcs_dbm_variant_type local_variant, fcs_var_base_reader *const reader,
332     fcs_state *const ret)
333 {
334     fcs_card new_top_most_cards[MAX_NUM_STACKS];
335 
336     fc_solve_delta_stater__init_card_states(self);
337 
338     for (int suit_idx = 0; suit_idx < FCS_NUM_SUITS; ++suit_idx)
339     {
340         const unsigned long foundation_rank =
341             fc_solve_var_base_reader_read(reader, FOUNDATION_BASE);
342 
343         for (unsigned long rank = 1; rank <= foundation_rank; ++rank)
344         {
345             RS_STATE((fcs_card)rank, (fcs_card)suit_idx) = OPT_IN_FOUNDATION;
346         }
347 
348         fcs_set_foundation(*ret, suit_idx, foundation_rank);
349     }
350 
351 #define IS_IN_FOUNDATIONS(card)                                                \
352     (fcs_card_rank(card) <= fcs_foundation_value(*ret, fcs_card_suit(card)))
353 
354     fcs_state *const init_state = self->init_state;
355 
356     const int orig_pos_opt =
357         (IS_BAKERS_DOZEN() ? OPT__BAKERS_DOZEN__ORIG_POS : OPT_ORIG_POS);
358 
359 #if MAX_NUM_FREECELLS > 0
360     const_SLOT(num_freecells, self);
361 #endif
362 
363     bool orig_top_most_cards[CARD_ARRAY_LEN] = {false};
364     for (size_t col_idx = 0; col_idx < self->num_columns; ++col_idx)
365     {
366         const_AUTO(col, fcs_state_get_col(*init_state, col_idx));
367 
368         if (fcs_col_len(col))
369         {
370             orig_top_most_cards[CARD_POS(fcs_col_get_card(col, 0))] = true;
371         }
372     }
373 
374     // Process the kings:
375     if (IS_BAKERS_DOZEN())
376     {
377         for (int suit_idx = 0; suit_idx < FCS_NUM_SUITS; ++suit_idx)
378         {
379             const fcs_card card = fcs_make_card(RANK_KING, (fcs_card)suit_idx);
380 
381             if (!IS_IN_FOUNDATIONS(card))
382             {
383                 SET_CARD_STATE(card, OPT__BAKERS_DOZEN__ORIG_POS);
384             }
385         }
386     }
387 
388 #if MAX_NUM_FREECELLS > 0
389     int next_freecell_idx = 0;
390 #endif
391     int next_new_top_most_cards = 0;
392     const int top_rank_for_iter = get_top_rank_for_iter(local_variant);
393     for (int rank = 1; rank <= top_rank_for_iter; ++rank)
394     {
395         for (int suit_idx = 0; suit_idx < FCS_NUM_SUITS; ++suit_idx)
396         {
397             const fcs_card card =
398                 fcs_make_card((fcs_card)rank, (fcs_card)suit_idx);
399             const_AUTO(card_char, fcs_card2char(card));
400 
401             if (IS_BAKERS_DOZEN())
402             {
403                 if ((self->bakers_dozen_topmost_cards_lookup[card_char >> 3] &
404                         (1 << (card_char & (8 - 1)))))
405                 {
406                     if (!IS_IN_FOUNDATIONS(card))
407                     {
408                         SET_CARD_STATE(card, OPT__BAKERS_DOZEN__ORIG_POS);
409                     }
410                     continue;
411                 }
412             }
413 
414             const int existing_opt =
415                 RS_STATE((fcs_card)rank, (fcs_card)suit_idx);
416 
417             if (rank == 1)
418             {
419                 if (existing_opt < 0)
420                 {
421                     RS_STATE((fcs_card)rank, (fcs_card)suit_idx) = orig_pos_opt;
422                 }
423             }
424             else
425             {
426                 const unsigned long base =
427                     (IS_BAKERS_DOZEN()
428                             ? NUM__BAKERS_DOZEN__OPTS
429                             : ((rank == RANK_KING) ? NUM_KING_OPTS : NUM_OPTS));
430                 const unsigned long item_opt =
431                     fc_solve_var_base_reader_read(reader, base);
432 
433                 if (existing_opt < 0)
434                 {
435                     RS_STATE((fcs_card)rank, (fcs_card)suit_idx) =
436                         (int)item_opt;
437 
438                     if (!IS_BAKERS_DOZEN())
439                     {
440                         if (item_opt == OPT_FREECELL)
441                         {
442 #if MAX_NUM_FREECELLS > 0
443                             fcs_put_card_in_freecell(
444                                 *ret, next_freecell_idx, card);
445                             ++next_freecell_idx;
446 #endif
447                         }
448                         else if (item_opt == OPT_TOPMOST)
449                         {
450                             if (!orig_top_most_cards[CARD_POS(card)])
451                             {
452                                 new_top_most_cards[next_new_top_most_cards++] =
453                                     card;
454                             }
455                         }
456                     }
457                 }
458             }
459         }
460     }
461 
462 #if MAX_NUM_FREECELLS > 0
463     for (; next_freecell_idx < num_freecells; ++next_freecell_idx)
464     {
465         fcs_empty_freecell(*ret, next_freecell_idx);
466     }
467 #endif
468 
469     for (size_t col_idx = 0; col_idx < self->num_columns; ++col_idx)
470     {
471         fcs_card top_card;
472         int top_opt;
473 
474         fcs_cards_column col = fcs_state_get_col(*ret, col_idx);
475         const_AUTO(orig_col, fcs_state_get_col(*init_state, col_idx));
476         const_AUTO(orig_col_len, fcs_col_len(orig_col));
477 
478         if (orig_col_len)
479         {
480             top_card = fcs_col_get_card(orig_col, 0);
481             top_opt = CARD_STATE(top_card);
482         }
483         else
484         {
485             top_card = fc_solve_empty_card;
486             top_opt = -1;
487         }
488 
489         if (fcs_card_is_empty(top_card) ||
490             (!((top_opt == OPT_TOPMOST) || (top_opt == orig_pos_opt))))
491         {
492             if (next_new_top_most_cards > 0)
493             {
494                 fcs_col_push_card(
495                     col, new_top_most_cards[--next_new_top_most_cards]);
496 
497                 delta_stater__fill_column_with_descendent_cards(
498                     self, local_variant, &col);
499             }
500         }
501         else
502         {
503             fcs_col_push_card(col, top_card);
504             var_AUTO(parent_card, top_card);
505             for (int pos = 1; pos < orig_col_len; ++pos)
506             {
507                 const fcs_card child_card = fcs_col_get_card(orig_col, pos);
508 
509                 if ((!IS_IN_FOUNDATIONS(child_card)) &&
510                     (CARD_STATE(child_card) ==
511                         calc_child_card_option(local_variant, parent_card,
512                             child_card PASS_ON_NOT_FC_ONLY(
513                                 self->sequences_are_built_by))))
514                 {
515                     fcs_col_push_card(col, child_card);
516                     parent_card = child_card;
517                 }
518                 else
519                 {
520                     break;
521                 }
522             }
523 
524             delta_stater__fill_column_with_descendent_cards(
525                 self, local_variant, &col);
526         }
527     }
528 #undef IS_IN_FOUNDATIONS
529 }
530 #undef IS_BAKERS_DOZEN
531 
fc_solve_delta_stater_decode_into_state_proto(const fcs_dbm_variant_type local_variant,fcs_delta_stater * const delta_stater,const rin_uchar * const enc_state,fcs_state_keyval_pair * const ret IND_BUF_T_PARAM (indirect_stacks_buffer))532 static inline void fc_solve_delta_stater_decode_into_state_proto(
533     const fcs_dbm_variant_type local_variant,
534     fcs_delta_stater *const delta_stater, const rin_uchar *const enc_state,
535     fcs_state_keyval_pair *const ret IND_BUF_T_PARAM(indirect_stacks_buffer))
536 {
537     fc_solve_var_base_reader_start(
538         &(delta_stater->r), enc_state, sizeof(fcs_encoded_state_buffer));
539 
540     fc_solve_state_init(ret, STACKS_NUM, indirect_stacks_buffer);
541 
542     fc_solve_delta_stater_decode(
543         delta_stater, local_variant, &(delta_stater->r), &(ret->s));
544 }
545 
fc_solve_delta_stater_encode_into_buffer(fcs_delta_stater * const delta_stater,const fcs_dbm_variant_type local_variant,fcs_state_keyval_pair * const state,unsigned char * const out_enc_state)546 static inline void fc_solve_delta_stater_encode_into_buffer(
547     fcs_delta_stater *const delta_stater,
548     const fcs_dbm_variant_type local_variant,
549     fcs_state_keyval_pair *const state, unsigned char *const out_enc_state)
550 {
551     fc_solve_var_base_writer_start(&(delta_stater->w));
552     fc_solve_delta_stater_set_derived(delta_stater, &(state->s));
553     fc_solve_delta_stater_encode_composite(
554         delta_stater, local_variant, &(delta_stater->w));
555     fc_solve_var_base_writer_get_data(&(delta_stater->w), out_enc_state);
556 }
557 #endif
558