ppwwpwpw-"""-",-"-,,-""-,"-""""","",,""","" wpwpwwwpwpwww-""-,"-""-,,-"-",-"""""","",",,"""",""",",",",,",,",",","""""""""""""""""""""""""""""""<333<3<<<<<<<3<3<3333333333333333333333333333333LDDDLLLLDLLLDLLLDLLLLDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\UUU\\\U\\UUU\\\U\\UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU,""",""",""",""",""",""",""","""""""""""""""""""""""""""""""""""<333<333<333<333<333<333<333<33333333333333333333333333333333333LDDDLDDDLDDDLDDDLDDDLDDDLDDDLDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\UUU\UUU\UUU\UUU\UUU\UUU\UUU\UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU,""",""",""",""",""",""",""","""""""""""""""""""""""""""""""""""<333<333<333<333<333<333<333<33333333333333333333333333333333333LDDDLDDDLDDDLDDDLDDDLDDDLDDDLDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\UUU\UUU\UUU\UUU\UUU\UUU\UUU\UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU,""",""",""",""","""""""""""""""""""""""<333<333<333<333<33333333333333333333333LDDDLDDDLDDDLDDDLDDDDDDDDDDDDDDDDDDDDDDD\UUU\UUU\UUU\UUU\UUUUUUUUUUUUUUUUUUUUUUUlfffllfflllflllllflllffllfffffffffffffffffffffffffffffff̜̙̙̙̙̼˼̻̻̻˼̻lffflffflffflffflffflffflffflffffffffffffffffffffffffffffffffffflffflffflffflffflffflffflffflffffffffffffffffffffffffffffffffffflffflffflffflffflfffffffffffffffffffffff ppppppppppppppppppppppppppppppppwwpwwwwpwwwwpwwwwpww@D@@DD@D@D@DD@ppwwpwwwww|ww| ppppppppppppppppppppppppppppppppwwpwwwwpwwwwpwwwwpwwwwppw|ww|ww|wwwwpw ppppppppppppppppppppppppppppppppwwpwwwwpwwwwpwwwwpww ppppppppppppppppppppppppwwpwwwwpwwwwpwwwwww=333=3<=3<=3<=3=3=3<333<3<33?ww|}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}w}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwtwwtwwtwwwtwwwwwDtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwGDwwtwGwwwwGwwwwwDtGwttGtwwwwwwwwwwtwtDwwwwwwGtwwDDtwwwwwwwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwwpppppppppwwtttwtwDDDGDDtwDtwttttttttwwwww}wwwwtttwGDtwDDGtwwDwwwwwwwwwwwwwwwwwwwwwwwwwwwtwtttGwwwwGwwwwwttttttDwwwww}wwwwDDtDGtGGDwwwwwDDwwwwwwwwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwtwGDwwtwwwtwwwwwttGwGwGtwwwwwwwwwDDtDGtGwGDGtwwwGtwwwwwwwwwwwwwwwwwwwwwwwwwtwwtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwDGtDGtGGDtGtwwDDwwwwwwwwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwGwwwwwGtwwDwwwwwGwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwwpppppppppwwttwwwwtwGtGwGtGtDtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwDtwDtwGwttGwtttwGwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwppp}ppwwwwppp}ppwwwwppp}ppwwttwwwwwtDwGwDwtwGwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwttwwwwDwGtDtGtGtwtwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpppp}pwwwwpppp}pwwwwpppp}pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww  wwww  wwww  wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpp}pp}pwwwwpp}pp}pwwwwpp}pp}pwwDwwwwwGtwwwwwwwwDwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww  wwww  wwww  wwttwwwwtwDwDwGtGtGwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpppwwwwpppwwwwpppwwDwwDtwtwGttttwttGwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwttwwwwtwtttttwDwGwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpppwwwwpppwwwwpppwwDwwwwwGtDtttGtGtDtwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpppwwwwpppwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwttwwwwDwwwwwwwGwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpp}pwwwwpp}pwwwwpp}pwwttwwwwttGtGtGtDtwGtDwDtGtwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwww wwww wwGwwDtwDtttDwttGwwttGtDtttwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwwpppppppppwwttwwwwDwDwwtDwGwwDtttttDwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwttwwwwttGtDwGtwtwwtDtttGtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwwwwwwwwwwwwwwwwwGwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwwpppppppppwwttwwwwDtwwwwGwwwwwwwGwtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwttwwwwGwDwGtDtttttGtDtwGwDwGtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwpppppppppwwwwpppppppppwwwwpppppppppwwGwwDtwGwttDwGwDwtttwGwtttttDww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwGwwwwwGwttwtGwtwtttwGwtttttwtw}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwGwwwwwDtttDwwttwGtGtwttGwttDwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwww wwww wwww wwww wwww wwww wwww wwww wwww}wwwwffffffff wwwwUUUUUUUU wwww wwwwDDDDDDDD wwww wwww wwwwffffffff wwwwUUUUUUUU wwwwwwwwfffffff wwww\UUUUU wwww̻ wwwwDDDDD wwww wwww wwwwfffffff wwww\UUUUU wwwwwwwwffffff wwwwU\UUUUU wwww̼ wwwwDDDDDD wwww wwww wwwwffffff wwwwU\UUUUU wwww}wwwfffff wwwwUUUUUUUU wwww˼ wwwwDDDDDD wwww wwww wwwwfffff wwwwUUUUUUUU www}wwwffffff wwww\UUUUU wwww̼ wwwwDDDDDD wwww wwww wwwwffffff wwww\UUUUU wwwwwwwfffffff wwwwU\UUUUU wwww̻ wwwwDDDDD wwww wwww wwwwfffffff wwwwU\UUUUU wwwwwwwwffffffff wwwwUUUUUUUU wwww wwwwDDDDDDDD wwww wwww wwwwffffffff wwwwUUUUUUUU wwww}wwww wwww wwww wwww wwww wwww wwww wwww wwww}wwww wwww"""""""" wwwwDDDDDDDD wwww wwww"""""""" wwww wwwwUUUUUUUU wwww wwwwwwww wwww""""""" wwwwDDDDD wwww wwww""""""" wwww̻ wwww\UUUUU wwww̻ wwwwwwww wwww",,""""" wwwwDDDDDD wwww wwww",,""""" wwww̼ wwwwU\UUUUU wwww̼ wwww}www wwww"""""" wwwwDDDDD wwww wwww"""""" wwww˼ wwwwUUUUUUUU wwww˼ www}www wwww",,""""" wwwwDDDOD wwww wwww",,""""" wwww̼ wwww\UUUUU wwww̼ wwwwwww wwww""""""" wwwwDDD wwww wwww""""""" wwww̻ wwwwU\UUUUU wwww̻ wwwwwwww wwww"""""""" wwwwDDDDO wwww wwww"""""""" wwww wwwwUUUUUUUU wwww wwww}wwww wwww wwwwDDDD wwww wwww wwww wwww wwww wwww}wwww33333333 wwww wwwwDDDD wwwwDDDDDDDD wwww"""""""" wwwwffffffff wwww wwww wwwwwwww3<<33333 wwww wwwwDDDDDO wwwwDDDDD wwww""""""" wwwwfffffff wwww wwww̜ wwwwwwww333333 wwww wwwwDDDDDD wwwwDDDDDD wwww",,""""" wwwwffffff wwww wwww wwww}www33333 wwww wwwwDDDDDDDD wwwwDDDDDD wwww"""""" wwwwfffff wwww wwww̜ www}www<33333 wwww wwwwDDDDDDDD wwwwDDDDDD wwww",,""""" wwwwffffff wwww wwww wwwwwww3<<33333 wwww wwwwDDDDDDDD wwwwDDDDD wwww""""""" wwwwfffffff wwww wwww̜ wwwwwwww33333333 wwww wwwwDDDDDDDD wwwwDDDDDDDD wwww"""""""" wwwwffffffff wwww wwww wwww}wwww wwww wwwwDDDDDDDD wwww wwww wwww wwww wwww wwww}wwww wwww wwwwDDDDDDDD wwww wwwwffffffff wwww33333333 wwww wwww"""""""" wwwwwwww̻ wwww̜ wwwwDDDDDDDD wwww̜ wwwwfffffff wwww3<<33333 wwww wwww""""""" wwwwwwww̼ wwww wwwwDDDDDDDD wwww wwwwffffff wwww333333 wwww wwww",,""""" wwww}www˼ wwww̜ wwwwDDDDDDDD wwww̜ wwwwfffff wwww33333 wwww wwww"""""" www}www̼ wwww wwwwDDDDDDDD wwww wwwwffffff wwww<33333 wwww wwww",,""""" wwwwwww̻ wwww̜ wwwwDDDDDDDD wwww̜ wwwwfffffff wwww3<<33333 wwww wwww""""""" wwwwwwwwǻ|wwwwǙ|wwwwDDDDDDDD|wwwwǙ|wwwwffffffff|wwww33333333|wwww|wwww""""""""|wwww}wwww}wwww}wwwwDDDDDDDD|wwww}wwww}wwww}wwww}wwww}wwww}wwwwUUUUUUUU|wwww|wwwwDDDDDDDD|wwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwwwwwww\UUUUU|wwww|wwwwDDDDDDDD|wwwwǙ̜|wwwwDDDDD|wwww3<<33333|wwww|wwww|wwwwwwwwU\UUUUU|wwww|wwwwDDDDDDDD|wwwwǙ|wwwwDDDDDD|wwww333333|wwww|wwww|wwww}wwwUUUUUUUU|wwww|wwwwDDDDDDDD|wwwwǙ̜|wwwwDDDDDD|wwww33333|wwww|wwww|www}www\UUUUU|wwww|wwww}wwwwǙ|wwwwDDDDDD|wwww<33333|wwww|wwww|wwwwwwwU\UUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ̜|wwwwDDDDD|wwww3<<33333|wwww|wwww|wwwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwww}wwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww}wwww}wwww}wwww}wwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww3<<33333|wwww|wwww|wwwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww333333|wwww|wwww|wwww}wwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333|wwww|wwww|www}wwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww<33333|wwww|wwww|wwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww3<<33333|wwww|wwww|wwwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwww}wwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwww}wwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwww}wwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|www}wwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwww}wwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwww}wwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwwwwwwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|wwww}wwwUUUUUUUU|wwww|wwwwwwwwwwwwwwwwwwǙ|wwwwDDDDDDDD|wwww33333333|wwww|wwww|www}www}wwww}wwwwwwwwwwwwwwwwww}wwww}wwww33333333|wwww|wwww|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww33333333|wwww|wwww|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww33333333|wwww|wwww|wwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww33333333|wwww|wwww|wwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww33333333|wwww|wwww|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww33333333|wwww|wwww|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww33333333|wwww|wwww|wwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww33333333|wwww|wwww|www}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwww}wwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}w}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}ww}w}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|wwV-- title: Collect-4 Solitaire -- author: denis1080 -- desc: The goal is to collapse all stacks of 4 same-colored cards. -- site: N/A -- license: MIT License -- version: 0.1 -- script: lua -- saveid: Collect4Solitaire_denis1080 -- The game menu options are populated from these entries -- Associated callbacks are triggered from the MENU() call. -- menu: View_instructions Toggle_controls_text Toggle_wins_text Reduce_difficulty Increase_difficulty Reset_saved_data -- This prevents the mouse from rendering so we can control when to render it -- based on whether we detect gamepad inputs or mouse movement. -- input: gamepad -- Boilerplate from https://github.com/rxi/classic -- -- classic -- -- Copyright (c) 2014, rxi -- -- This module is free software; you can redistribute it and/or modify it under -- the terms of the MIT license. See LICENSE for details. -- ---@diagnostic disable-next-line: redundant-parameter local Object={}Object.__index=Object;function Object:new()end;function Object:extend()local b={}for c,d in pairs(self)do if c:find("__")==1 then b[c]=d end end;b.__index=b;b.super=self;setmetatable(b,self)return b end;function Object:implement(...)for e,b in pairs({...})do for c,d in pairs(b)do if self[c]==nil and type(d)=="function"then self[c]=d end end end end;function Object:is(f)local g=getmetatable(self)while g do if g==f then return true end;g=getmetatable(g)end;return false end;function Object:__tostring()return"Object"end;function Object:__call(...)local h=setmetatable({},self)h:new(...)return h end -- END of boilerplate from https://github.com/rxi/classic local Timer = { scheduled_fns = {} } function Timer.update() for i = #Timer.scheduled_fns, 1, -1 do local scheduled_fn = Timer.scheduled_fns[i] scheduled_fn.frames_left = scheduled_fn.frames_left - 1 if scheduled_fn.frames_left == 0 then scheduled_fn.fn() table.remove(Timer.scheduled_fns, i) end end end function Timer.after(frames, fn) table.insert(Timer.scheduled_fns, { frames_left = frames, fn = fn }) end function Timer.next_frame(fn) Timer.after(1, fn) end local save_data = { total_wins = pmem(0), current_difficulty = pmem(1), hardest_difficulty_unlocked = pmem(2), hide_controls_text = pmem(3), hide_wins_text = pmem(4) } -- Called from every other function that modifies the save data. function save_data.save() pmem(0, save_data.total_wins) pmem(1, save_data.current_difficulty) pmem(2, save_data.hardest_difficulty_unlocked) pmem(3, save_data.hide_controls_text) pmem(4, save_data.hide_wins_text) end function save_data.reset() save_data.total_wins = 0 save_data.current_difficulty = 0 save_data.hardest_difficulty_unlocked = 0 save_data.hide_controls_text = 0 save_data.hide_wins_text = 0 save_data.save() end function save_data.would_unlock_next_difficulty() return save_data.current_difficulty < 3 and save_data.current_difficulty == save_data.hardest_difficulty_unlocked end function save_data.increment_total_wins() save_data.total_wins = save_data.total_wins + 1 if save_data.would_unlock_next_difficulty() then save_data.increase_difficulty() save_data.hardest_difficulty_unlocked = save_data.current_difficulty end save_data.save() end function save_data.can_decrease_difficulty_from_menu() return save_data.current_difficulty > 0 end function save_data.decrease_difficulty() save_data.current_difficulty = math.max(save_data.current_difficulty - 1, 0) save_data.save() end function save_data.can_increase_difficulty_from_menu() return save_data.current_difficulty < save_data.hardest_difficulty_unlocked end function save_data.increase_difficulty() save_data.current_difficulty = math.min(save_data.current_difficulty + 1, 3) save_data.save() end function save_data.toggle_controls_text() if save_data.hide_controls_text == 0 then save_data.hide_controls_text = 1 else save_data.hide_controls_text = 0 end save_data.save() end function save_data.toggle_wins_text() if save_data.hide_wins_text == 0 then save_data.hide_wins_text = 1 else save_data.hide_wins_text = 0 end save_data.save() end local card_info = { { top_left_index = 32, color = "purple" }, { top_left_index = 35, color = "red" }, { top_left_index = 38, color = "orange" }, { top_left_index = 41, color = "yellow" }, { top_left_index = 44, color = "light green" }, { top_left_index = 96, color = "dark green" }, { top_left_index = 99, color = "dark blue" }, { top_left_index = 102, color = "light blue" }, { top_left_index = 105, color = "light grey" }, { top_left_index = 108, color = "dark grey" }, } Card = Object:extend() function Card:new(top_left_index, color) self.top_left_index = top_left_index self.color = color end function Card:draw(x, y) spr(self.top_left_index, x, y, 0, 1, 0, 0, 3, 4) end Card_back = Object:extend() Card_back.card_info = { purple = 12, red = 14, orange = 224, yellow = 226, ["light green"] = 228, ["dark green"] = 230, ["dark blue"] = 232, ["light blue"] = 234, ["light grey"] = 236, ["dark grey"] = 238, } function Card_back:new(color) self.card_back_top_left_index = 172 self.color = color end function Card_back:draw(x, y) spr(self.card_back_top_left_index, x, y, 0, 1, 0, 0, 3, 4) spr(Card_back.card_info[self.color], x + 1, y + 7, 0, 1, 0, 0, 2, 2) end Lock = Object:extend() function Lock:new() self.lock_top_left_index = 3 end function Lock:draw(x, y) spr(self.lock_top_left_index, x + 1, y + 7, 0, 1, 0, 0, 2, 2) end -- A stack or a free cell can be represented as a Pile. Their main difference is -- the max amount of cards they can accept. Pile = Object:extend() function Pile:new(x, y, is_free_cell) self.x = x self.y = y self.bg_top_left_index = 163 self.cards = {} if is_free_cell then self.max_cards = 1 else -- As a result of the game rules, we can't get into a situation with -- more than 8 cards in a stack anyways. self.max_cards = 8 end end function Pile:draw() spr(self.bg_top_left_index, self.x, self.y, 7, 1, 0, 0, 3, 4) -- Draw the special Card_back or the Lock. if self.locked_with then self.locked_with:draw(self.x + 1, self.y + 1) return end for i, card in ipairs(self.cards) do card:draw(self.x + 1, self.y + (i - 1) * 8 + 1) end end Cursor = Object:extend() -- During MODE_SELECT_FROM, we aren't holding cards. The pointer can be placed -- on any empty pile, a pile's top card, or cards directly underneath that match -- the top card's color. From this mode, we can grab cards under the pointer. Cursor.MODE_SELECT_FROM = 0 -- During MODE_SELECT_TO, we are holding cards. Therefore, drawing of the -- pointer and held cards is offset to the right relative to the selected pile. -- The pointer can be placed on any empty pile or immediately above a pile's top -- card. From this mode, when held cards match with the top card in the pile and -- respects the pile's max cards, we can set cards from our pointer. Cursor.MODE_SELECT_TO = 1 -- Changes in pointed stack depending on movement direction Cursor.move_up_from_stacks = {nil, nil, nil, nil, 1, 1, 1, 2, 3, 4, 4, 4} Cursor.move_down_from_free_cells = {7, 8, 9, 10} Cursor.move_left = {5, 1, 2, 3, 5, 5, 6, 7, 8, 9, 10, 11} Cursor.move_right = {2, 3, 4, 12, 6, 7, 8, 9, 10, 11, 12, 12} -- It's important that piles are initialized already. function Cursor:new(piles) self.piles = piles self.mode = Cursor.MODE_SELECT_FROM self.completed_groups = 0 self.held_cards = nil self.held_cards_from_pile = nil -- Start pointer at top of leftmost stack. self.pointer_on_pile = 5 self.pointer_on_pile_level = 5 end function Cursor:is_on_free_cell() return self.pointer_on_pile >= 1 and self.pointer_on_pile <= 4 end function Cursor:draw() local pile = self.piles[self.pointer_on_pile] local draw_height = self.pointer_on_pile_level local x_offset = 0 local y_offset = 0 if self.mode == Cursor.MODE_SELECT_TO then -- Push the cursor and held cards up relative to the cards in the pile -- underneath. draw_height = draw_height + 1 -- Help highlight that the cards are held by offsetting the cursor and -- held cards. x_offset = 6 y_offset = 2 for index, card in ipairs(self.held_cards) do card:draw(pile.x + x_offset, pile.y + y_offset + (#pile.cards + index - 1) * 8 + 1) end end -- When the cursor would be drawn below the empty pile, bump it down so -- it's drawn in the pile. if draw_height == 0 then draw_height = 1 end spr(17, pile.x + x_offset + 10, pile.y + y_offset - 3 + draw_height * 8, 0) end function Cursor:up() if not self:is_on_free_cell() then if self.pointer_on_pile_level <= 1 or self.mode == Cursor.MODE_SELECT_TO then self.pointer_on_pile = Cursor.move_up_from_stacks[self.pointer_on_pile] local pile = self.piles[self.pointer_on_pile] self.pointer_on_pile_level = #pile.cards self.pointer_is_on_free_cell = true else local pile = self.piles[self.pointer_on_pile] local initial_color = pile.cards[self.pointer_on_pile_level].color self.pointer_on_pile_level = self.pointer_on_pile_level - 1 local next_color = pile.cards[self.pointer_on_pile_level].color if initial_color ~= next_color then self.pointer_on_pile = Cursor.move_up_from_stacks[self.pointer_on_pile] pile = self.piles[self.pointer_on_pile] self.pointer_on_pile_level = #pile.cards end end end end function Cursor:down() if self:is_on_free_cell() then self.pointer_on_pile = Cursor.move_down_from_free_cells[self.pointer_on_pile] local pile = self.piles[self.pointer_on_pile] self.pointer_on_pile_level = #pile.cards elseif self.mode == Cursor.MODE_SELECT_FROM then local pile = self.piles[self.pointer_on_pile] if self.pointer_on_pile_level < #pile.cards then self.pointer_on_pile_level = self.pointer_on_pile_level + 1 end end end function Cursor:left() self.pointer_on_pile = Cursor.move_left[self.pointer_on_pile] local pile = self.piles[self.pointer_on_pile] self.pointer_on_pile_level = #pile.cards end function Cursor:right() self.pointer_on_pile = Cursor.move_right[self.pointer_on_pile] local pile = self.piles[self.pointer_on_pile] self.pointer_on_pile_level = #pile.cards end local game = nil function Cursor:confirm() if self.mode == Cursor.MODE_SELECT_FROM then local pile = self.piles[self.pointer_on_pile] if pile.locked_with or #pile.cards == 0 then return end self.held_cards = {} for _ = #pile.cards, self.pointer_on_pile_level, -1 do table.insert(self.held_cards, 1, table.remove(pile.cards)) end self.pointer_on_pile_level = #pile.cards self.held_cards_from_pile = pile self.mode = Cursor.MODE_SELECT_TO elseif self.mode == Cursor.MODE_SELECT_TO then local to_pile = self.piles[self.pointer_on_pile] if to_pile.locked_with then return end -- Can't move if there's a card in the to_pile of a different color if #to_pile.cards > 0 then local card = to_pile.cards[#to_pile.cards] if card.color ~= self.held_cards[1].color then return end end -- The check of 4-of-the-same-color happens before considering the -- pile's max_cards. local temp_combined_cards = {} for i = 1, #to_pile.cards do table.insert(temp_combined_cards, to_pile.cards[i]) end for i = 1, #self.held_cards do table.insert(temp_combined_cards, self.held_cards[i]) end if #temp_combined_cards == 4 and temp_combined_cards[1].color == temp_combined_cards[2].color and temp_combined_cards[1].color == temp_combined_cards[3].color and temp_combined_cards[1].color == temp_combined_cards[4].color then -- Group of 4 cards of the same color completed! to_pile.locked_with = Card_back(temp_combined_cards[1].color) to_pile.cards = {} self.pointer_on_pile_level = 0 self.held_cards = {} self.mode = Cursor.MODE_SELECT_FROM -- If completing a group of 4 in a non-free-cell stack, unlock a -- free cell (assuming there is one to unlock). if not self:is_on_free_cell() then for i = 1, 4 do if self.piles[i].locked_with and self.piles[i].locked_with:is(Lock) then self.piles[i].locked_with = nil break end end end self.completed_groups = self.completed_groups + 1 -- Check victory condition if game.cursor.completed_groups == 10 then game.is_won = true save_data.increment_total_wins() Timer.after(120, function() game = Game() end) end return end -- Can't move if trying to set more than one card in a free cell if #self.held_cards + #to_pile.cards > to_pile.max_cards then return end for _ = 1, #self.held_cards do table.insert(to_pile.cards, table.remove(self.held_cards, 1)) end self.pointer_on_pile_level = #to_pile.cards self.mode = Cursor.MODE_SELECT_FROM end end function Cursor:cancel() if self.mode == Cursor.MODE_SELECT_TO then for _ = 1, #self.held_cards do table.insert(self.held_cards_from_pile.cards, table.remove(self.held_cards, 1)) end self.held_cards = nil self.held_cards_from_pile = nil local pile = self.piles[self.pointer_on_pile] self.pointer_on_pile_level = #pile.cards self.mode = Cursor.MODE_SELECT_FROM end end -- TODO complete mouse support AND combine with keyboard/controller support -- As it stands, MousePointer... -- * Keeps track of whether the mouse was pressed last frame -- * Can be told to render or not based on gamepad input detection/mouse movement -- * Can draw an arrow or hand (suggesting that the item underneath is clickable) MousePointer = Object:extend() function MousePointer:draw_arrow() spr(16, self.x, self.y, 0) end function MousePointer:draw_hand() spr(17, self.x - 3, self.y, 0) end function MousePointer:new() self.left_last_frame = false self.left_pressed = false self.x = nil self.y = nil self.is_active = true self.draw_func = MousePointer.draw_arrow end function MousePointer:update() -- x, y, left, middle, right, scrollx, scrolly local x, y, left_this_frame, _, _, _, _ = mouse() if self.x ~= nil and x ~= self.x then self.is_active = true end if self.y ~= nil and y ~= self.y then self.is_active = true end if left_this_frame and not self.left_last_frame then self.left_pressed = true else self.left_pressed = false end self.x = x self.y = y self.left_last_frame = left_this_frame end function MousePointer:draw() if self.is_active then self:draw_func() end end Instructions = Object:extend() Instructions.return_to_game_message = "Press A/B/X/Y to return to the game." function Instructions:new() self.page = 1 self.num_pages = 4 end function Instructions:next_page() self.page = self.page % self.num_pages + 1 end function Instructions:previous_page() self.page = (self.page - 2) % self.num_pages + 1 end local buttons = { UP = 0, DOWN = 1, LEFT = 2, RIGHT = 3, A = 4, B = 5, X = 6, Y = 7 } function Instructions:update() if btnp(buttons.LEFT) then self:previous_page() elseif btnp(buttons.RIGHT) then self:next_page() elseif btnp(buttons.A) or btnp(buttons.B) or btnp(buttons.X) or btnp(buttons.Y) then Timer.next_frame(function () game:resume() end) end end function Instructions:print_page_x_of_y(x) local str = "Page " .. x .. "/" .. self.num_pages print(str, 98, 122, 4) end local function print_lines(x, start_y, color, ...) local y = start_y for _, line in ipairs({...}) do print(line, x, y, color) y = y + 7 end end function Instructions:draw() cls(7) map(0, 0, 30, 17, 0, 0, 0) -- print("<", 14, 122, 4) -- print(">", 223, 122, 4) -- print("|", 120, 122, 4) print("<", 84, 122, 4) print(">", 153, 122, 4) if self.page == 1 then print_lines(14, 10, 4, "Collect-4 Solitaire", "", "The goal is to collapse all stacks of 4", "same-colored cards.", "", "Controls", "", "Directions - Move cursor across piles", "A - Pick up or place cards", "B - Reset held cards to their last", " position", "X - Reset the game", "Y - Show full instructions", "", Instructions.return_to_game_message) self:print_page_x_of_y(1) elseif self.page == 2 then print_lines(14, 10, 4, "Slots, cells, and card movement", "", "Multiple cards can be stacked in the 8", "piles at the bottom. The 4 cells at the", "top can only accommodate 1 card.", "", "Only same-colored top card(s) of a stack", "or cell cards can be picked up at any", "given time. Cards can only be placed on", "same-colored cards, an empty pile, or a", "free cell.", "", "", "", Instructions.return_to_game_message) self:print_page_x_of_y(2) elseif self.page == 3 then print_lines(14, 10, 4, "Grouping same-colored cards", "", "A stack is collapsed and considered", "complete when it contains 4 same-", "colored cards (and only those cards).", "If the stack is collapsed in a pile, a", "locked cell (if there were any) is", "unlocked. A stack can also be collapsed", "in a cell by moving the necessary same-", "colored cards there in one movement.", "The pile or cell becomes unusable", "after that.", "", "", Instructions.return_to_game_message) self:print_page_x_of_y(3) elseif self.page == 4 then print_lines(14, 10, 4, "Game Menu", "", "Reduce_difficulty - Resets game with an", " additional cell unlocked (if available)", "Increase_difficulty - Resets game with", " and additional cell locked (if", " difficulty was unlocked previously)", "Reset_saved_data - reinitializes cart", " data to easiest difficulty and 0 wins", "", "", "", "", Instructions.return_to_game_message) self:print_page_x_of_y(4) end end Game = Object:extend() function Game:new() self.piles = {} -- Free cells will occupy piles 1 - 4 for i = 1, 4 do local fc = Pile(i * 28 + 40, 13, true) -- 40 = 5 (border) + 8 (gap) + 28 (card gap width) - 1 (offset to line up cell with card) table.insert(self.piles, fc) end -- locked_pile_indexes describes the starting lock pattern. local locked_pile_indexes = {} if save_data.current_difficulty == 1 then locked_pile_indexes = {4} elseif save_data.current_difficulty == 2 then locked_pile_indexes = {3, 4} elseif save_data.current_difficulty == 3 then locked_pile_indexes = {2, 3, 4} end for _, locked_pile in ipairs(locked_pile_indexes) do self.piles[locked_pile].locked_with = Lock() end local cards = {} for i = 1, 10 do local info = card_info[i] for _ = 1, 4 do table.insert(cards, Card(info.top_left_index, info.color)) end end for i = 1, 40 do local j = math.random(40) local temp = cards[i] cards[i] = cards[j] cards[j] = temp end -- Stacks will occupy piles 5 - 12 for x = 1, 8 do -- 12 = 5 (border) + 8 (gap) - 1 (offset to line up cell with card) -- 50 = 5 (border) + 8 (gap) + 30 (card height) + 8 (gap) - 1 (offset to line up cell with card) local stack = Pile((x - 1) * 28 + 12, 50, false) for y = 1, 5 do local i = (x - 1) * 5 + y table.insert(stack.cards, cards[i]) end table.insert(self.piles, stack) end self.cursor = Cursor(self.piles) end function Game:view_instructions() self.instructions = Instructions() end function Game:resume() self.instructions = nil end function Game:update() if self.instructions then self.instructions:update() return end if btnp(buttons.UP) then self.cursor:up() elseif btnp(buttons.DOWN) then self.cursor:down() elseif btnp(buttons.LEFT) then self.cursor:left() elseif btnp(buttons.RIGHT) then self.cursor:right() elseif btnp(buttons.A) then self.cursor:confirm() elseif btnp(buttons.B) then self.cursor:cancel() elseif btnp(buttons.X) then -- Quick-reset Timer.next_frame(function () game = Game() end) elseif btnp(buttons.Y) then self:view_instructions() end end local function print_lines_small(x, start_y, color, ...) local y = start_y for _, line in ipairs({...}) do print(line, x, y, color, false, 1, true) y = y + 7 end end function Game:draw() if self.instructions then self.instructions:draw() return end cls(7) map(0, 0, 30, 17, 0, 0, 0) for _, p in ipairs(self.piles) do p:draw() end if save_data.hide_wins_text == 0 then local wins = tostring(save_data.total_wins) if save_data.total_wins >= 1000 then wins = tostring(math.floor(save_data.total_wins / 1000)) .. "k" end print("Wins: " .. wins, 14, 14, 4) end self.cursor:draw() if self.is_won then print("You win!", 80, 96, 12, false, 2) end if save_data.hide_controls_text == 0 then spr(166, 176, 12, 0, 1, 0, 0, 4, 1) print_lines_small(176, 12, 4, " - Move", "A - Select", "B - Cancel", "X - Reset game", "Y - Instructions") end end local function toggle_controls_text() -- Delaying the call to reset due to game engine bug preventing pmem calls from the menu: -- https://github.com/nesbox/TIC-80/issues/1958 (fix will be in the next release though!) Timer.next_frame(function() save_data.toggle_controls_text() game:resume() end) end local function toggle_wins_text() -- ...bug prevents pmem calls from the menu Timer.next_frame(function() save_data.toggle_wins_text() game:resume() end) end local function decrease_difficulty() if save_data.can_decrease_difficulty_from_menu() then -- ...bug prevents pmem calls from the menu Timer.next_frame(function() save_data.decrease_difficulty() game = Game() end) end end local function increase_difficulty() if save_data.can_increase_difficulty_from_menu() then -- ...bug prevents pmem calls from the menu Timer.next_frame(function() save_data.increase_difficulty() game = Game() end) end end local function reset_saved_data() -- ...bug prevents pmem calls from the menu Timer.next_frame(function() save_data.reset() game = Game() end) end local game_menu ={ function() game:view_instructions() end, toggle_controls_text, toggle_wins_text, decrease_difficulty, increase_difficulty, reset_saved_data } -- The "menu" metadata at the top of the file sets the game menu options. function MENU(i) game_menu[i+1]() end -- local mouse_pointer = MousePointer() -- mouse_pointer:update() -- mouse_pointer:draw() function BOOT() game = Game() end function TIC() Timer.update() game:update() game:draw() end