@@ -291,7 +291,7 @@ static void apply_conduit(struct solution *solution, struct board *board, struct
291291 remove_atom (board , (struct atom_ref_at_position ){ a , p });
292292 } else {
293293 // bonds are consumed even if the atoms aren't.
294- * a &= ~(ALL_BONDS & ~ RECENT_BONDS & conduit -> atoms [base + k ].atom );
294+ * a &= ~(REAL_BONDS & conduit -> atoms [base + k ].atom );
295295 }
296296 add_produced_atom_collider (board , mechanism_relative_position (other_side , delta .u , delta .v , 1 ));
297297 }
@@ -1449,7 +1449,7 @@ static void match_repeating_output_with_chain_atoms(struct board *board, struct
14491449 if (!found_chain_atom )
14501450 return ;
14511451 }
1452- if ((a & (ANY_ATOM | ( ALL_BONDS & ~ RECENT_BONDS ) )) != output .atom )
1452+ if ((a & (ANY_ATOM | REAL_BONDS )) != output .atom )
14531453 return ;
14541454 }
14551455 }
@@ -1458,217 +1458,96 @@ static void match_repeating_output_with_chain_atoms(struct board *board, struct
14581458 io -> maximum_feed_rate = maximum_feed_rate ;
14591459}
14601460
1461- static bool mark_output_position (struct board * board , struct vector pos )
1461+ static bool is_ignored_output_position (struct input_output * io , struct vector pos )
14621462{
1463- atom * a = lookup_atom (board , pos );
1464- if ((* a & VISITED ) || !(* a & VALID ) || (* a & REMOVED ))
1463+ // atoms cannot appear outside the vertical bounds of the
1464+ // infinite product.
1465+ if (pos .v < io -> min_v || pos .v > io -> max_v )
14651466 return false;
1466- * a |= VISITED ;
1467- size_t capacity = board -> marked .capacity ;
1468- while (board -> marked .length >= capacity )
1469- capacity = 4 * (capacity + 12 ) / 3 ;
1470- if (capacity != board -> marked .capacity ) {
1471- struct vector * positions = realloc (board -> marked .positions ,
1472- sizeof (struct movement ) * capacity );
1473- if (!positions )
1474- abort ();
1475- board -> marked .positions = positions ;
1476- board -> marked .capacity = capacity ;
1477- }
1478- board -> marked .positions [board -> marked .length ++ ] = pos ;
1479- return true;
1467+ // atoms cannot appear between atoms in a row of the infinite
1468+ // product. the row_min_v and row_max_v arrays track the
1469+ // disallowed range of positions for each row.
1470+ size_t row = pos .v - io -> min_v ;
1471+ return pos .u < io -> row_min_u [row ] || pos .u > io -> row_max_u [row ];
14801472}
14811473
1482- static void consume_output_with_overlap (struct solution * solution , struct board * board , struct input_output * io )
1474+ static void consume_output (struct solution * solution , struct board * board , struct input_output * io , int output_index )
14831475{
1476+ bool fail_on_wrong_output = board -> fails_on_wrong_output_mask & (1ULL << io -> puzzle_index );
1477+ bool fail_on_wrong_bonds = board -> fails_on_wrong_output_bonds_mask & (1ULL << io -> puzzle_index );
1478+ bool wrong_output = false;
1479+ bool wrong_bonds = false;
1480+ bool repeating = io -> type & REPEATING_OUTPUT ;
1481+
14841482 struct mechanism m = { 0 , io -> atoms [io -> center_atom_index ].position };
14851483 struct molecule * molecule = get_molecule (board , get_atom (board , m , 0 , 0 ));
14861484
1487- if (molecule -> size != io -> number_of_atoms )
1485+ if (!repeating && molecule -> size != io -> number_of_atoms )
1486+ return ;
1487+ if (repeating && molecule -> size < io -> number_of_atoms - 1 )
14881488 return ;
14891489
14901490 for (uint32_t i = 0 ; i < molecule -> size ; ++ i ) {
1491- bool match = false;
14921491 struct atom_ref_at_position a = molecule -> atoms [i ];
1492+ if (repeating && is_ignored_output_position (io , a .position ))
1493+ continue ;
1494+ if (!repeating && (* a .atom & GRABBED ))
1495+ return ;
1496+ atom target = 0 ;
14931497 for (uint32_t j = 0 ; j < io -> number_of_atoms ; ++ j ) {
14941498 if (vectors_equal (a .position , io -> atoms [j ].position )) {
1495- match = ( * a . atom & ( ANY_ATOM | ( ALL_BONDS & ~ RECENT_BONDS ) | GRABBED )) = = io -> atoms [j ].atom ;
1499+ target = io -> atoms [j ].atom ;
14961500 break ;
14971501 }
14981502 }
1499- if (!match )
1503+ if (!target )
1504+ return ;
1505+ atom differences = * a .atom ^ target ;
1506+
1507+ if ((differences & REAL_BONDS ) && repeating && !wrong_bonds ) {
1508+ // bonds that lead to an ignored position are allowed
1509+ for (int dir = 0 ; dir < 6 ; ++ dir ) {
1510+ struct vector d = u_offset_for_direction (dir );
1511+ struct vector neighbor = { a .position .u + d .u , a .position .v + d .v };
1512+ if (is_ignored_output_position (io , neighbor ))
1513+ differences &= ~(BOND_LOW_BITS << dir );
1514+ }
1515+ }
1516+
1517+ wrong_output = wrong_output || ((differences & ANY_ATOM ) && !(target & VARIABLE_OUTPUT ));
1518+ wrong_bonds = wrong_bonds || (differences & REAL_BONDS );
1519+ if (!(fail_on_wrong_output || fail_on_wrong_bonds ) && (wrong_output || wrong_bonds ))
15001520 return ;
15011521 }
15021522
1503- remove_molecule (board , molecule );
1504- io -> number_of_outputs ++ ;
1523+ if (fail_on_wrong_output && wrong_output ) {
1524+ report_collision (board , io -> atoms [0 ].position , "output didn't match" );
1525+ board -> wrong_output_index = output_index ;
1526+ }
1527+ if (fail_on_wrong_bonds && wrong_bonds ) {
1528+ report_collision (board , io -> atoms [0 ].position , "output bonds didn't match" );
1529+ board -> wrong_output_index = output_index ;
1530+ }
1531+ if (wrong_output || wrong_bonds )
1532+ return ;
1533+
1534+ // if the output is a match remove the output and increment the output counter.
1535+ if (repeating ) {
1536+ io -> number_of_outputs = io -> number_of_repetitions * io -> outputs_per_repetition ;
1537+ if (board -> chain_mode == EXTEND_CHAIN )
1538+ match_repeating_output_with_chain_atoms (board , io );
1539+ } else {
1540+ remove_molecule (board , molecule );
1541+ io -> number_of_outputs ++ ;
1542+ }
15051543}
15061544
15071545static void consume_outputs (struct solution * solution , struct board * board )
15081546{
15091547 for (size_t i = 0 ; i < solution -> number_of_inputs_and_outputs ; ++ i ) {
15101548 struct input_output * io = & solution -> inputs_and_outputs [i ];
1511- if (!(io -> type & OUTPUT ))
1512- continue ;
1513- bool fails_on_wrong_output = board -> fails_on_wrong_output_mask & (1ULL << io -> puzzle_index );
1514-
1515- if (board -> number_of_overlapped_atoms > 0
1516- && io -> number_of_atoms > 1
1517- && !fails_on_wrong_output
1518- && !(io -> type & REPEATING_OUTPUT )) {
1519- consume_output_with_overlap (solution , board , io );
1520- continue ;
1521- }
1522-
1523- bool wrong_output = fails_on_wrong_output ;
1524- board -> marked .length = 0 ;
1525- // first, check the entire output to see if it matches.
1526- bool match = true;
1527- for (uint32_t j = 0 ; j < io -> number_of_atoms ; ++ j ) {
1528- atom output = io -> atoms [j ].atom ;
1529- if (output & REPEATING_OUTPUT_PLACEHOLDER )
1530- continue ;
1531- atom * a = lookup_atom (board , io -> atoms [j ].position );
1532- // printf("checking %d %d... ", io->atoms[j].position.u, io->atoms[j].position.v);
1533- if (!(* a & VALID ) || (* a & REMOVED )) {
1534- // printf("no atom\n");
1535- match = false;
1536- wrong_output = false;
1537- break ;
1538- }
1539- // variable outputs match any atom.
1540- if (output & VARIABLE_OUTPUT ) {
1541- output &= ~VARIABLE_OUTPUT ;
1542- output |= * a & ANY_ATOM ;
1543- }
1544- if (io -> type & REPEATING_OUTPUT ) {
1545- uint64_t bond_mask = (((output & RECENT_BONDS ) >> RECENT_BOND ) * BOND_LOW_BITS ) & ~RECENT_BONDS ;
1546- if ((* a & (ANY_ATOM | bond_mask )) != (output & (ANY_ATOM | bond_mask ))) {
1547- // printf("did not match at %d %d: %llx vs %llx\n", io->atoms[j].position.u, io->atoms[j].position.v, (*a & (ANY_ATOM | bond_mask)), output & (ANY_ATOM | bond_mask));
1548- match = false;
1549- break ;
1550- }
1551- mark_output_position (board , io -> atoms [j ].position );
1552- } else if (fails_on_wrong_output ) {
1553- if ((* a & (ALL_BONDS & ~RECENT_BONDS )) != (output & ALL_BONDS & ~RECENT_BONDS ) || (* a & GRABBED )) {
1554- match = false;
1555- wrong_output = false;
1556- break ;
1557- }
1558- if ((* a & ANY_ATOM ) != (output & ANY_ATOM )) {
1559- match = false;
1560- // this could be a wrong output.
1561- }
1562- } else {
1563- if ((* a & (ANY_ATOM | (ALL_BONDS & ~RECENT_BONDS ))) != output || (* a & GRABBED )) {
1564- // printf("did not match at %d %d: %llx vs %llx\n", io->atoms[j].position.u, io->atoms[j].position.v, (*a & (ANY_ATOM | (ALL_BONDS & ~RECENT_BONDS))), output);
1565- match = false;
1566- break ;
1567- }
1568- }
1569- // printf("match!\n");
1570- }
1571- bool wrong_output_bonds = false;
1572- if (board -> fails_on_wrong_output_bonds_mask & (1ULL << io -> puzzle_index )) {
1573- wrong_output_bonds = true;
1574- for (uint32_t j = 0 ; j < io -> number_of_atoms ; ++ j ) {
1575- if (io -> atoms [j ].atom & REPEATING_OUTPUT_PLACEHOLDER )
1576- continue ;
1577- atom * a = lookup_atom (board , io -> atoms [j ].position );
1578- if (!(* a & VALID ) || (* a & REMOVED ) || (* a & GRABBED )) {
1579- wrong_output_bonds = false;
1580- break ;
1581- }
1582- mark_output_position (board , io -> atoms [j ].position );
1583- }
1584- // if wrong_output_bonds is set at this point, then the molecule
1585- // covers the output completely. the goal now is to determine
1586- // whether there are any bonds leading out of the output shape. if
1587- // so, the footprint doesn't match, so the output doesn't have wrong
1588- // bonds.
1589- for (uint32_t j = 0 ; !match && wrong_output_bonds && j < io -> number_of_atoms ; ++ j ) {
1590- if (io -> atoms [j ].atom & REPEATING_OUTPUT_PLACEHOLDER )
1591- continue ;
1592- atom a = * lookup_atom (board , io -> atoms [j ].position );
1593- for (int bond_direction = 0 ; bond_direction < 6 ; ++ bond_direction ) {
1594- if (!(a & (BOND_LOW_BITS << bond_direction ) & ~RECENT_BONDS ))
1595- continue ;
1596- struct vector next = io -> atoms [j ].position ;
1597- struct vector d = u_offset_for_direction (bond_direction );
1598- next .u += d .u ;
1599- next .v += d .v ;
1600- atom b = * lookup_atom (board , next );
1601- if (!(b & VISITED )) {
1602- wrong_output_bonds = false;
1603- break ;
1604- }
1605- }
1606- }
1607- }
1608- // validating infinite products requires visiting all the atoms in the
1609- // molecule. that's what this loop does.
1610- if (io -> type & REPEATING_OUTPUT ) {
1611- size_t cursor = 0 ;
1612- while (match && cursor < board -> marked .length ) {
1613- struct vector p = board -> marked .positions [cursor ++ ];
1614- atom a = * lookup_atom (board , p );
1615- if (a & IS_CHAIN_ATOM && board -> chain_mode == EXTEND_CHAIN ) {
1616- // ensure atoms stay within the vertical region forever.
1617- struct chain_atom ca = board -> chain_atoms [lookup_chain_atom (board , p )];
1618- if (ca .current_position .v - ca .original_position .v != 0 ) {
1619- match = false;
1620- break ;
1621- }
1622- }
1623- for (int bond_direction = 0 ; bond_direction < 6 ; ++ bond_direction ) {
1624- if (!(a & (BOND_LOW_BITS << bond_direction ) & ~RECENT_BONDS ))
1625- continue ;
1626- struct vector next = p ;
1627- struct vector d = u_offset_for_direction (bond_direction );
1628- next .u += d .u ;
1629- next .v += d .v ;
1630- if (!mark_output_position (board , next ))
1631- continue ;
1632- // atoms cannot appear outside the vertical bounds of the
1633- // infinite product.
1634- if (next .v < io -> min_v || next .v > io -> max_v ) {
1635- match = false;
1636- break ;
1637- }
1638- // atoms cannot appear between atoms in a row of the infinite
1639- // product. the row_min_v and row_max_v arrays track the
1640- // disallowed range of positions for each row.
1641- size_t row = next .v - io -> min_v ;
1642- if (next .u >= io -> row_min_u [row ] && next .u <= io -> row_max_u [row ]) {
1643- match = false;
1644- break ;
1645- }
1646- }
1647- }
1648- }
1649- // reset the visited flag for all the marked atoms.
1650- for (size_t j = 0 ; j < board -> marked .length ; ++ j ) {
1651- atom * a = lookup_atom (board , board -> marked .positions [j ]);
1652- * a &= ~VISITED ;
1653- }
1654- if (match && (io -> type & REPEATING_OUTPUT ) && board -> chain_mode == EXTEND_CHAIN )
1655- match_repeating_output_with_chain_atoms (board , io );
1656- // if the output is a match remove the output and increment the output counter.
1657- if (match ) {
1658- if (io -> type & REPEATING_OUTPUT )
1659- io -> number_of_outputs = io -> number_of_repetitions * io -> outputs_per_repetition ;
1660- else {
1661- for (uint32_t j = 0 ; j < io -> number_of_atoms ; ++ j )
1662- remove_atom (board , (struct atom_ref_at_position ){ lookup_atom (board , io -> atoms [j ].position ), io -> atoms [j ].position });
1663- io -> number_of_outputs ++ ;
1664- }
1665- } else if (wrong_output ) {
1666- report_collision (board , io -> atoms [0 ].position , "output didn't match" );
1667- board -> wrong_output_index = i ;
1668- } else if (wrong_output_bonds ) {
1669- report_collision (board , io -> atoms [0 ].position , "output bonds didn't match" );
1670- board -> wrong_output_index = i ;
1671- }
1549+ if (io -> type & OUTPUT )
1550+ consume_output (solution , board , io , i );
16721551 }
16731552}
16741553
@@ -2061,7 +1940,6 @@ void destroy(struct solution *solution, struct board *board)
20611940 free (board -> flag_reset );
20621941 free (board -> movements .movements );
20631942 free (board -> moving_atoms .atoms_at_positions );
2064- free (board -> marked .positions );
20651943 free (board -> overlapped_atoms );
20661944 free (board -> chain_atoms );
20671945 free (board -> chain_atom_table );
0 commit comments