41c41
<     if rm.req_ct%2==1 and rm.opt_ct==0 and len(rm.poss)>1: #an odd room
---
>     if len(rm.req_gatesqs)%2==1 and len(rm.opt_gatesqs)==0 and len(rm.poss)>1: #an odd room
52,53d51
<     if rm.must_clean and len(rm.gatesqs) == 1 and len(rm.poss) > 1:
<         return True, False
54a53,57
> 
>     if len(rm.gatesqs) == 1 and len(rm.poss) > 1:
>         if rm.must_clean:
>             return True, False
> 
72,73c75,76
<     if not config.use_new_caching:
<         return
---
>     #if not config.use_new_caching:
>         #return
75,77d77
<         if time_allocated < 0:
<             import ipdb;ipdb.set_trace()
<         #not 100% valid but maybe useful.
99,100c99,100
<     if not config.use_new_caching:
<         return
---
>     #if not config.use_new_caching:
>         #return
116c116
<                     print('skip cause already devoted more, %s' % rm.progenitor.hashid[:6])
---
>                     #print('skip cause already devoted more, %s' % rm.progenitor.hashid[:6])
120,122c120,122
<                     print('devoting more time, %s %s %s' % (rm.progenitor.hashid[:6], timeout_cache[key], time_allocated))
<                     if key == global_last_devotion:
<                         import ipdb;ipdb.set_trace()
---
>                     #print('devoting more time, %s %s %s' % (rm.progenitor.hashid[:6], timeout_cache[key], time_allocated))
>                     #if key == global_last_devotion:
>                         #import ipdb;ipdb.set_trace()
123a124,126
>             elif key in getone_cache:
>                 if getone_cache[key] == False:
>                     import ipdb;ipdb.set_trace()
128,130c131,133
<     #if cacheres is not None:
<         #devotions[rm.temp_key].append(('cache res', (cacheres== TIMELIMIT and 'timelimit') or \
<                                    #(cacheres == True and getone and 'cache getone true') or (cacheres and '%d sols' % len(cacheres)) or (getone and 'getone fail' or 'no sols')))
---
>     if config.save_devotions and cacheres is not None:
>         devotions[rm.temp_key].append(('cache res', (cacheres== TIMELIMIT and 'timelimit') or \
>                                    (cacheres == True and getone and 'cache getone true') or (cacheres and '%d sols' % len(cacheres)) or (getone and 'getone fail' or 'no sols')))
151c154
<     #devotions[rm.temp_key].append(('time allocated', toplevel and 'toplevel' or 'not toplevel', time_allocated))
---
>     if config.save_devotions:devotions[rm.temp_key].append(('time allocated', toplevel and 'toplevel' or 'not toplevel', time_allocated))
154c157
<         #devotions[rm.temp_key].append(('have easy solve res', toplevel and 'toplevel' or 'not toplevel', res))
---
>         if config.save_devotions:devotions[rm.temp_key].append(('have easy solve res', toplevel and 'toplevel' or 'not toplevel', res))
171a175
>     if rm.hashid.startswith('77e52a'):import ipdb;ipdb.set_trace()
173c177
<     #devotions[rm.temp_key].append(('breakdown res', toplevel and 'toplevel' or 'not toplevel', (res== TIMELIMIT and 'timelimit') or (res == True and getone and 'getone true') or (res and '%d sols' % len(res)) or (getone and 'getone fail' or 'no sols')))
---
>     if config.save_devotions:devotions[rm.temp_key].append(('breakdown res', toplevel and 'toplevel' or 'not toplevel', (res== TIMELIMIT and 'timelimit') or (res == True and getone and 'getone true') or (res and '%d sols' % len(res)) or (getone and 'getone fail' or 'no sols')))
177c181
<                 rm.smh(override_text = '%s-parent' % rm.progenitor.hashid[:6])
---
>                 rm.smh(override_text = '%s-parent--' % rm.progenitor.hashid[:6])
179c183
<                 text = '%s-child-' % rm.progenitor.hashid[:6]
---
>                 text = '%s-child--' % rm.progenitor.hashid[:6]
181c185
<                     if rm.kind == 'subroom':
---
>                     if rm.kind == 'subrooms':
279,284c283
<     this does not have access to board, so as long as you cache enough in the key,
<     it's cacheable.
<     #by this point, the room may just be a virtual one - 
<     made of a list of sqs & gates.  we need to actually make a row structure for it.
< 
<     if all sols timesout, but we have at least some sols, shouldn't we save at least that has_sol is true?
---
>     actual work of getting all solutions for a room.
291,294c290
<     dobreak=False
<     doshow = False
<     breakdown_solutions=set() #this should only have sol format hings added to it.
<     #have_found_clean=False
---
>     breakdown_solutions=set()
296a293,294
>         #if rm.all_ct > 30:
>             #import ipdb;ipdb.set_trace()
309,310c307,310
<         if toplevel:rm.start_progress += 1
<         if toplevel and not getone:rm.solvetext += '-' + str(rm.start_progress)
---
>         if toplevel:
>             rm.start_progress += 1
>         if toplevel and not getone:
>             rm.solvetext += '-' + str(rm.start_progress)
312a313
>             import ipdb;ipdb.set_trace()
314d314
<         took_start = indv == START  #so that subrooms will know their rules.
317d316
<         #if doshow:print('print before gate check %0.6f' % (time.clock() - st))
325,330d323
<         #if 0 and have_found_clean:  #this is messed up actually. cause say we find
<             ##a clean sol for a simple room going out exit A, in exit B.  there
<             ##could be a dirty sol with reverse exit rules, which is required to solve the board.
<             ##remove all inner START from remaining starts.
<             #badstarts = [st for st in starts if st[1] == START]
<             #for bs in badstarts: starts.remove(bs)
333,334c326,327
<         illegal_count = get_base_illegal_count(rm, took_start)
<         continuations=go_until_deadend_with_path(rm, startsq, indv=outdv, took_start = took_start, already_covered = [], illegal_count  = illegal_count )
---
>         illegal_count = get_base_illegal_count(rm, indv == START)
>         continuations=go_until_deadend_with_path(rm, startsq, indv=outdv, took_start = indv == START, already_covered = [], illegal_count  = illegal_count )
342c335
<             if took_start and took_end:#this means we took the start and end on the same path.
---
>             if indv == START and took_end:
347,348c340,342
<                 #the case where we cover a whole room in one se path, but it is a subroom/ not whole board is still annoying.
<             partial_solutions=set((((took_start,startsq,outdv,tuple(path),tuple(covered),stop,took_end,),),),) #a set of tuples of solsteps.
---
>                 #the case where we cover a whole room in one se path, 
>                 # but it is a subroom/ not whole board is still annoying.
>             partial_solutions=set((((indv == START,startsq,outdv,tuple(path),tuple(covered),stop,took_end,),),),) #a set of tuples of solsteps.
355d348
<             #prior_remaining = set([sq for sq in rm.allsqs if rm.isopen(sq)])
357,358c350,351
<             #if doshow:print('after set %0.6f' % (time.clock() - st))
<             #we do the subcalculations in there, to find failed subrooms earlier so we don't keep making other subrooms.
---
>             #we do the subcalculations in there, to find failed 
>             # subrooms earlier so we don't keep making other subrooms.
363d355
<                 #if rm.curopen > 3:import ipdb;ipdb.set_trace()
364a357
>                 #just_did_optgate = False
371c364
<             if (rm.progenitor.start_progress < rm.progenitor.start_cutoff) and time.time()>rm.timelimit:
---
>             if (rm.progenitor.start_progress < rm.progenitor.start_cutoff) and time.time() > rm.timelimit:
377c370,371
<             #very important that the order is right here.  subrooms_ok can timelimit; previously this was causing
---
>             #very important that the order is right here.  
>             #subrooms_ok can timelimit; previously this was causing
382c376,377
<                 #if this set of subrooms doesn't work, so just die.  none of the others will work either.
---
>                 #if this set of subrooms doesn't work, so just die.  
>                 # none of the others will work either.
385c380,381
<             if subrooms_ok: #we are both checking for illegal subrooms; and if not, saving the good ones into a solution that's being built.
---
>             if subrooms_ok: #we are both checking for illegal subrooms; 
>                 # and if not, saving the good ones into a solution that's being built.
563a560
> #@profile
565a563,676
>     #print('go until deadend with path rm %s startsq %s indv %s already covered %s' % \
>           #(rm, startsq, indv, already_covered))
>     if (rm.progenitor.start_progress < rm.progenitor.start_cutoff) and time.time()>rm.timelimit:
>         return TIMELIMIT
>     if not already_path:
>         already_path=[]
>     if indv in EXITS:
>         end=startsq
>         covered= tuple()
>         dvs=[]
>         path=()
>         violation=False
>     else:
>         end, covered, dvs, path, violation = rm.multimove_path(startsq,indv)
>     if violation:
>         rm.unset_many(covered)
>         return []
>     total_covered= already_covered[:]
>     total_covered.extend(covered)
>     total_path=already_path[:]
>     total_path.extend(path)
>     res=[]
>     this_orth= []  #just check the recently seen sqs. duh.
>     made_deadend=False
>     deadend_sq=None
>     allopen = []
>     for sq in rm.allsqs:
>         if rm.isopen(sq):
>             allopen.append(sq)
>     allopen = set(allopen)
>     for c in covered:
>         for o in orth(c):
>             #if rm.isopen(o):
>             if o in allopen:
>                 if o not in rm.opt_gatesqs:
>                     this_orth.append(o)
>                 
>     for o in orth(startsq):
>         #if rm.isopen(o):
>         if o in allopen:
>             if o not in rm.opt_gatesqs:
>                 this_orth.append(o)
> 
>     this_orth = set(this_orth)
>     #checking just hit a wall with alleys on both side.
>     if path:
>         righty = add(end, (path[-1] + 1) % 4)
>         lefty = add(end, (path[-1] -1) % 4)
>         if (isalley_ordeadend_rows(righty, rm = rm) and righty not in rm.gatesqs) and \
>            (isalley_ordeadend_rows(lefty, rm = rm) and lefty not in rm.gatesqs):
>             if not rm.can_end and not rm.can_both:  #this is vital.  imaging hitting this shape:
>                 #111111
>                 #11...1
>                 #22X1.1
>                 #11...1
>                 #111111  it's perfectly fine to bump two alleys like that.
>                 #you'll walk one and end in the other.
>                 rm.unset_many(covered)
>                 return []
>     for o in this_orth:
>         opennei=[n for n in orth(o) if n in allopen]
>         #this is a simple alley check for orth of the path we just went on.
>         if dist(end, o) != 1:
>             if len(opennei)==1 and o not in rm.req_gatesqs:
>                 illegal_count += 1
>             if len(opennei)==0:
>                 #checking a room-isolated square; only okay if its in opt!
>                 if o in rm.req_gatesqs:  #totally cut off (from room's perspective) a reqgate
>                     illegal_count += 1
>                 else:  #either it's a req gate and yo ucan't end, or it's an inner square.
>                     made_deadend=True
>                     deadend_sq=o
>                     break
>     if made_deadend:
>         rm.unset_many(covered)
>         return []
>     if end in rm.gatesqs:
>         res.append([total_path, total_covered, end, False]) #went out a gate (or maybe not! if its a pararoom you can keep going inside the room.)
>         if not this_orth:  #also use this to decide if they can take end there.
>             #this works because of multimove; they must have started their last move from a decision point which
>             #meant open on either side; since we include start in orth calculation, we are good.
>             if rm.can_end or rm.can_both:
>                 res.append([total_path, total_covered, end, True]) #also had the option to just end at the gate.
>     elif not dvs:
>         if indv == OUT:
>             res.append([total_path, total_covered, end, False])
>         elif not this_orth:
>             #you blockaded yourrm, not at an exit! oopsy
>             if rm.can_end or rm.can_both:
>                 res.append([total_path, total_covered, end, True])
>     else:
>         #stopped with two choices, but not at a gate.  go on.
>         if illegal_count > 1:
>             rm.unset_many(covered)
>             return []
>         for dv in dvs:
>             thisres=go_until_deadend_with_path(rm, end, dv, took_start = took_start, already_covered = total_covered, already_path = total_path,
>                                                illegal_count = illegal_count)
>             if thisres==TIMELIMIT:
>                 rm.unset_many(covered)
>                 return TIMELIMIT
>             res.extend(thisres)
>     if illegal_count > 1:
>         rm.unset_many(covered)
>         return []
>     rm.unset_many(covered)
>     return res
> 
> 
> 
> 
> #@profile
> def go_until_deadend_with_path_old(rm, startsq, indv, already_covered, took_start, already_path=None, illegal_count = 0):
>     """returns a list of ([total_path, total_covered, end, took_end])"""
653c764
<             thisres=go_until_deadend_with_path(rm, end, dv, took_start = took_start, already_covered = total_covered, already_path = total_path,
---
>             thisres=go_until_deadend_with_path_old(rm, end, dv, took_start = took_start, already_covered = total_covered, already_path = total_path,
664d774
< 
