# MIT License # # Copyright (c) 2026 # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # # This applies to original game source and primitive artwork. Bitmap glyph data # is derived from Noto Sans CJK under SIL Open Font License 1.1, and is covered # by assets/NOTO-LICENSE.txt instead of MIT. """蜜蜂の引っ越し・教材1/5。解説はLESSON.mdを参照。""" from dataclasses import dataclass,replace,asdict FLOWER_X = 2 TARGET = 3 @dataclass(frozen=True) class State: t:int=0 x:int=0 mode:str='idle' load:int=0 stock:int=3 nectar:int=1 outcome:str='playing' def apply(s,a): if s.outcome!='playing':return s,'terminal' if a==('send',): if s.mode!='idle':return s,'busy' if s.stock==0:return s,'empty' return replace(s,mode='out'),'send' if a!=('tick',):return s,'invalid' q=replace(s,t=s.t+1) if s.mode=='out': x=s.x+1 if x==FLOWER_X:q=replace(q,x=x,mode='back',load=1,stock=s.stock-1) else:q=replace(q,x=x) elif s.mode=='back': x=s.x-1 if x==0:q=replace(q,x=0,mode='idle',load=0,nectar=s.nectar+s.load) else:q=replace(q,x=x) return replace(q,outcome='won' if q.nectar>=TARGET else 'playing'),'tick' STAGE=1 class App: def __init__(self):self.reset() def reset(self):self.state=State();self.message='READY';self.gated=False def press(self,key): if key=='R':self.reset() elif key=='Z':self.state,self.message=apply(self.state,('send',)) elif key=='SPACE':self.state,self.message=apply(self.state,('tick',)) def control(self,pressed,held): if self.gated: if not pressed and not held:self.gated=False return if len(pressed|held)>1:self.gated=True;self.message='RELEASE ALL KEYS';return if len(pressed)==1:self.press(next(iter(pressed)));self.gated=True def draw(self,p): s=self.state;p.cls(1) p.text(12,12,'MOVING BEES / STAGE 1 / ONE BEE DELIVERY',10) p.text(12,32,'TICK '+str(min(s.t,9999))+('+' if s.t>9999 else '')+' / '+s.mode.upper(),7) p.rect(12,70,616,160,3) p.rect(48,182,24,25,9);p.rect(56,191,8,16,4) x=60+FLOWER_X*100;p.line(x,115,x,190,11);p.circ(x,115,15,10);p.circ(x,115,5,4) p.text(x-20,145,'STOCK '+str(s.stock),7) bx=60+s.x*100;p.circ(bx-5,91,5,6);p.circ(bx+5,91,5,6);p.rect(bx-7,95,15,7,10);p.line(bx,95,bx,101,0) for i in range(FLOWER_X+1):p.text(60+i*100,214,str(i),7) p.text(12,251,'HIVE NECTAR '+str(s.nectar)+' / TARGET '+str(TARGET)+' / LOAD '+str(s.load),10) p.text(12,278,'TOTAL '+str(s.nectar+s.stock+s.load)+' = HIVE + STOCK + LOAD',6) p.text(12,306,'PICKUP IS NOT DELIVERY. NECTAR ENTERS THE HIVE ON RETURN.',7) p.text(12,326,'ONE BEE / ONE OPEN FLOWER / NO RESERVATIONS OR MOVING HIVE.',13) p.text(12,386,'RESULT '+s.outcome.upper()+' / '+self.message.upper(),11 if s.outcome=='won' else 7) p.text(12,426,'Z: SEND / SPACE: ONE TICK / R: RESET / ESC: QUIT',7) p.text(12,444,'RELEASE EACH KEY BEFORE THE NEXT. TIME DOES NOT RUN BY ITSELF.',13) def demo_keys():return ('Z','SPACE','SPACE') def main(headless=False,screenshot=None,demo=False): # Pyxel may change cwd. Resolve and validate all destinations before importing it. from pathlib import Path if screenshot: target=Path(screenshot).resolve() if target.suffix.lower()!='.png' or not target.parent.is_dir() or target.exists(): raise ValueError('use a new .png in an existing directory') screenshot=str(target) import pyxel as p p.init(640,464,title='Moving bees lesson '+str(STAGE),fps=30,display_scale=1,headless=headless) app=App();sequence=demo_keys() if demo else () keys={name:getattr(p,'KEY_'+name) for name in ('LEFT','RIGHT','TAB','SPACE','Z','C','X','R','1','2','3')} limit=max(20,2*len(sequence)+8) def update(): if p.btnp(p.KEY_ESCAPE) or headless and p.frame_count>=limit: if screenshot:app.draw(p);p.screen.save(screenshot,1) p.quit();return if headless: if demo: index=p.frame_count//2 pressed={sequence[index]} if p.frame_count%2==0 and index