Как я Марс спасал или небольшой квест на питоне

    Привет, Мир!

    Август 2018


    На улице стоит жаркое лето, плавно подходящее к концу, а я сижу в прохладной комнате с ноутбуком и серфлю интернет в поиске интересных вещей. Потеряв надежду найти что-либо стоящее внимания, вдруг, натыкаюсь в одной из довольно популярных околоайтишных групп вконтакте пост со ссылкой на еще один айти ресурс. Квест показался мне интересным, ведь это почти классический текстовый квест в космическом сеттинге, да еще и программировать тут нужно!



    Поэтому в этой статье речь пойдет о том, как я играл решал этот квест.

    По ссылке нас ждет введение в квест следующим текстом:

    2022 год. Ракета-носитель KASP со 120 колонистами на борту и электромобилем с открытым верхом приближается к Марсу.

    Перед началом посадки выясняется, что одна из систем корабля неисправна. Центр управления полётами пытается передать инструкции экипажу, но из-за солнечной бури сообщение приходит на KASP беспорядочными фрагментами. От вас зависит судьба миссии — расшифруйте сообщение и следуйте инструкциям ЦУП.


    После выбора языка – доступны C++, C# и Python, нас ждет страница с «сообщением от ЦУПа», которое представляет собой набор символов в определенной кодировке. Чтобы пройти на следующий этап нужно декодировать сообщение, отсортировав куски сообщения в порядке увеличения длины сообщения и ввести md5-хэш полученного сообщения.
    Но что сортировать, скажете вы? То, что это сообщение представляет собой некую кодировку становится понятно сразу, какую именно можно легко понять, воспользовавшись похожим ресурсом, например.

    Поняв, что это base64, начинаем его декодировать. Я сделал это на python'е:

    Декодируем base64:
    import base64
    code = """W3siaWQiOjMxLCJzIjoib255OiAxIG1hbiJ9LHsiaWQiOjI3LCJzIjoiZGUgYWxsIHNwZSJ9LHsiaWQiOjYxLCJzIjoicyB3aWxsIGZhYyJ9LHsiaWQiOjU1LCJzIjoib3V0IG1lbiwgYSJ9LHsiaWQiOjQyLCJzIjoibnRpc3RzLlxuMi4ifSx7ImlkIjoxOCwicyI6Im4uIE91ciBzb2MifSx7ImlkIjo3MCwicyI6InRoZSBwbGFuZXQifSx7ImlkIjo1LCJzIjoiLiBPbmUgb3BlciJ9LHsiaWQiOjY0LCJzIjoiIGNvbmRpdGlvbiJ9LHsiaWQiOjQ0LCJzIjoieSBoYXZlIHRvICJ9LHsiaWQiOjQ5LCJzIjoic3QgNTElIG9mICJ9LHsiaWQiOjU4LCJzIjoiaXN0cyBtdXN0ICJ9LHsiaWQiOjcxLCJzIjoiLiBTZWxlY3QgcCJ9LHsiaWQiOjU5LCJzIjoiYmUgbWFsZS5cbjMifSx7ImlkIjo4NSwicyI6ImhlIGZvbGxvd2kifSx7ImlkIjo0MywicyI6IiBUaGUgY29sb24ifSx7ImlkIjo3MywicyI6IiByZXNpbGllbmMifSx7ImlkIjoyNCwicyI6InNlbGVjdGlvbi4ifSx7ImlkIjo2MiwicyI6ImUgYWxsIGtpbmQifSx7ImlkIjo0NywicyI6Im5lcmF0aW9ucywifSx7ImlkIjoxMDUsInMiOiJlb3BsZSB3aG8gIn0seyJpZCI6MTAzLCJzIjoicm1hdGlvbiwgcyJ9LHsiaWQiOjIyLCJzIjoiaWFzIHRvIHNpbSJ9LHsiaWQiOjg2LCJzIjoibmcgZm9ybWF0OiJ9LHsiaWQiOjIsInMiOiJwJ3MgbGFuZGVyIn0seyJpZCI6MzYsInMiOiIgY29tcHV0ZXIgIn0seyJpZCI6NDgsInMiOiIgc28gYXQgbGVhIn0seyJpZCI6MTYsInMiOiJmb3IgTWFycyBjIn0seyJpZCI6NzUsInMiOiIwIGFuZCBtb3JlIn0seyJpZCI6MTEsInMiOiIuIFlvdSBoYXZlIn0seyJpZCI6OTUsInMiOiJlbWFsZSA9IDAsIn0seyJpZCI6NjgsInMiOiJwbGUgc2hvdWxkIn0seyJpZCI6NDEsInMiOiJzLCAxNCBzY2llIn0seyJpZCI6NywicyI6IiwgYnV0IGlzIHUifSx7ImlkIjo5OCwicyI6ImUgcmF0aW5nXG5lIn0seyJpZCI6MCwicyI6IlR3byBvZiB0aGUifSx7ImlkIjoxMDEsInMiOiIuXG5cbktub3dpbmcifSx7ImlkIjozMywicyI6Im9rcywgNCBlbGUifSx7ImlkIjo5NywicyI6IjtSZXNpbGllbmMifSx7ImlkIjo1MSwicyI6Im9sb25pc3RzIG0ifSx7ImlkIjoxMDgsInMiOiJkIFBsYW5ldC4ifSx7ImlkIjoxMDAsInMiOiJicmVhayAtIFxcbiJ9LHsiaWQiOjYwLCJzIjoiLiBDb2xvbmlzdCJ9LHsiaWQiOjQwLCJzIjoiOCBtZWNoYW5pYyJ9LHsiaWQiOjc4LCJzIjoiMTAwLlxuXG5UaGUgIn0seyJpZCI6MSwicyI6IiB0aHJlZSBzaGkifSx7ImlkIjoxMywicyI6IjAgbW9zdCBhcHAifSx7ImlkIjoxMiwicyI6IiB0byBwaWNrIDQifSx7ImlkIjo5MywicyI6IklEO1Byb2Zlc3MifSx7ImlkIjoxMDQsInMiOiJlbGVjdCA0MCBwIn0seyJpZCI6MzksInMiOiI1IG1lZGljcywgIn0seyJpZCI6OCwicyI6Im5hYmxlIHRvIGMifSx7ImlkIjo1MiwicyI6InVzdCBiZSBmZW0ifSx7ImlkIjo5NiwicyI6IiBtYWxlID0gMSkifSx7ImlkIjo4OSwicyI6ImZlbWFsZSA9IDAifSx7ImlkIjoxMCwicyI6IiBjb2xvbmlzdHMifSx7ImlkIjo2NSwicyI6InMuIE9ubHkgdGgifSx7ImlkIjozNSwicyI6ImdpbmVlcnMsIDUifSx7ImlkIjoyMSwicyI6InJlZSBjcml0ZXIifSx7ImlkIjoxNywicyI6Im9sb25pc2F0aW8ifSx7ImlkIjo1NCwicyI6IiBmb3JnZXQgYWIifSx7ImlkIjoxNSwicyI6ImFuZGlkYXRlcyAifSx7ImlkIjo4NywicyI6IiBJRDtQcm9mZXMifSx7ImlkIjozNywicyI6InN5c3RlbSBzcGUifSx7ImlkIjo3OSwicyI6ImZpbGUgYXR0YWMifSx7ImlkIjo5MCwicyI6IiwgbWFsZSA9IDEifSx7ImlkIjoyOSwicyI6InNzZW50aWFsIGYifSx7ImlkIjoxMDcsInMiOiJpemUgdGhlIFJlIn0seyJpZCI6NiwicyI6ImF0ZXMgZnVsbHkifSx7ImlkIjo2NiwicyI6ImUgbW9zdCByZXMifSx7ImlkIjozMiwicyI6ImFnZXIsIDMgY28ifSx7ImlkIjo2MywicyI6InMgb2YgaGFyc2gifSx7ImlkIjo0LCJzIjoidW5jdGlvbmluZyJ9LHsiaWQiOjgyLCJzIjoidGFpbnMgYSBsaSJ9LHsiaWQiOjM4LCJzIjoiY2lhbGlzdHMsICJ9LHsiaWQiOjc0LCJzIjoiZSByYXRpbmcgNiJ9LHsiaWQiOjc3LCJzIjoiZnJvbSAxIHRvICJ9LHsiaWQiOjE5LCJzIjoiaW9sb2dpc3RzICJ9LHsiaWQiOjMsInMiOiJzIGFyZSBtYWxmIn0seyJpZCI6NTMsInMiOiJhbGUuIERvbid0In0seyJpZCI6MjMsInMiOiJwbGlmeSB0aGUgIn0seyJpZCI6MjgsInMiOiJjaWFsaXN0cyBlIn0seyJpZCI6ODgsInMiOiJzaW9uO1NleCAoIn0seyJpZCI6OTQsInMiOiJpb247U2V4IChmIn0seyJpZCI6MTA2LCJzIjoid2lsbCBjb2xvbiJ9LHsiaWQiOjI1LCJzIjoiXG5cbjEuIENyZXcgIn0seyJpZCI6MTAyLCJzIjoiIHRoaXMgaW5mbyJ9LHsiaWQiOjkxLCJzIjoiKTtSZXNpbGllbiJ9LHsiaWQiOjgxLCJzIjoicyB0YXNrIGNvbiJ9LHsiaWQiOjU3LCJzIjoiJSBvZiBjb2xvbiJ9LHsiaWQiOjc2LCJzIjoiIG9uIHNjYWxlICJ9LHsiaWQiOjMwLCJzIjoib3IgdGhlIGNvbCJ9LHsiaWQiOjgwLCJzIjoiaGVkIHRvIHRoaSJ9LHsiaWQiOjE0LCJzIjoicm9wcmlhdGUgYyJ9LHsiaWQiOjcyLCJzIjoiZW9wbGUgd2l0aCJ9LHsiaWQiOjY5LCJzIjoiIGNvbG9uaXplICJ9LHsiaWQiOjksInMiOiJvbnRhaW4gMTIwIn0seyJpZCI6MjAsInMiOiJkZWZpbmVkIHRoIn0seyJpZCI6NjcsInMiOiJpbGllbnQgcGVvIn0seyJpZCI6NTAsInMiOiJzZWxlY3RlZCBjIn0seyJpZCI6OTksInMiOiJ0Yy4gXG5MaW5lICJ9LHsiaWQiOjgzLCJzIjoic3Qgb2YgY29sbyJ9LHsiaWQiOjU2LCJzIjoidCBsZWFzdCAzMCJ9LHsiaWQiOjg0LCJzIjoibmlzdHMgaW4gdCJ9LHsiaWQiOjM0LCJzIjoiY3RyaWNhbCBlbiJ9LHsiaWQiOjQ1LCJzIjoiZXhpc3QgZm9yICJ9LHsiaWQiOjkyLCJzIjoiY2UgcmF0aW5nXG4ifSx7ImlkIjoyNiwicyI6Im11c3QgaW5jbHUifSx7ImlkIjo0NiwicyI6InNldmVyYWwgZ2UifV0="""
    
    def base64ToString(b):
        return base64.b64decode(b).decode('utf-8')
    
    decodedJSON = base64ToString(code)
    print(decodedJSON)
    

    После вывода становится понятно, что данные представлены в формате JSON:

    Такой джейсон получаем:
    [{"id":31,"s":"ony: 1 man"},{"id":27,"s":"de all spe"},{"id":61,"s":"s will fac"},{"id":55,"s":"out men, a"},{"id":42,"s":"ntists.\n2."},{"id":18,"s":"n. Our soc"},{"id":70,"s":"the planet"},{"id":5,"s":". One oper"},{"id":64,"s":" condition"},{"id":44,"s":"y have to "},{"id":49,"s":"st 51% of "},{"id":58,"s":"ists must "},{"id":71,"s":". Select p"},{"id":59,"s":"be male.\n3"},{"id":85,"s":"he followi"},{"id":43,"s":" The colon"},{"id":73,"s":" resilienc"},{"id":24,"s":"selection."},{"id":62,"s":"e all kind"},{"id":47,"s":"nerations,"},{"id":105,"s":"eople who "},{"id":103,"s":"rmation, s"},{"id":22,"s":"ias to sim"},{"id":86,"s":"ng format:"},{"id":2,"s":"p's lander"},{"id":36,"s":" computer "},{"id":48,"s":" so at lea"},{"id":16,"s":"for Mars c"},{"id":75,"s":"0 and more"},{"id":11,"s":". You have"},{"id":95,"s":"emale = 0,"},{"id":68,"s":"ple should"},{"id":41,"s":"s, 14 scie"},{"id":7,"s":", but is u"},{"id":98,"s":"e rating\ne"},{"id":0,"s":"Two of the"},{"id":101,"s":".\n\nKnowing"},{"id":33,"s":"oks, 4 ele"},{"id":97,"s":";Resilienc"},{"id":51,"s":"olonists m"},{"id":108,"s":"d Planet."},{"id":100,"s":"break - \\n"},{"id":60,"s":". Colonist"},{"id":40,"s":"8 mechanic"},{"id":78,"s":"100.\n\nThe "},{"id":1,"s":" three shi"},{"id":13,"s":"0 most app"},{"id":12,"s":" to pick 4"},{"id":93,"s":"ID;Profess"},{"id":104,"s":"elect 40 p"},{"id":39,"s":"5 medics, "},{"id":8,"s":"nable to c"},{"id":52,"s":"ust be fem"},{"id":96,"s":" male = 1)"},{"id":89,"s":"female = 0"},{"id":10,"s":" colonists"},{"id":65,"s":"s. Only th"},{"id":35,"s":"gineers, 5"},{"id":21,"s":"ree criter"},{"id":17,"s":"olonisatio"},{"id":54,"s":" forget ab"},{"id":15,"s":"andidates "},{"id":87,"s":" ID;Profes"},{"id":37,"s":"system spe"},{"id":79,"s":"file attac"},{"id":90,"s":", male = 1"},{"id":29,"s":"ssential f"},{"id":107,"s":"ize the Re"},{"id":6,"s":"ates fully"},{"id":66,"s":"e most res"},{"id":32,"s":"ager, 3 co"},{"id":63,"s":"s of harsh"},{"id":4,"s":"unctioning"},{"id":82,"s":"tains a li"},{"id":38,"s":"cialists, "},{"id":74,"s":"e rating 6"},{"id":77,"s":"from 1 to "},{"id":19,"s":"iologists "},{"id":3,"s":"s are malf"},{"id":53,"s":"ale. Don't"},{"id":23,"s":"plify the "},{"id":28,"s":"cialists e"},{"id":88,"s":"sion;Sex ("},{"id":94,"s":"ion;Sex (f"},{"id":106,"s":"will colon"},{"id":25,"s":"\n\n1. Crew "},{"id":102,"s":" this info"},{"id":91,"s":");Resilien"},{"id":81,"s":"s task con"},{"id":57,"s":"% of colon"},{"id":76,"s":" on scale "},{"id":30,"s":"or the col"},{"id":80,"s":"hed to thi"},{"id":14,"s":"ropriate c"},{"id":72,"s":"eople with"},{"id":69,"s":" colonize "},{"id":9,"s":"ontain 120"},{"id":20,"s":"defined th"},{"id":67,"s":"ilient peo"},{"id":50,"s":"selected c"},{"id":99,"s":"tc. \nLine "},{"id":83,"s":"st of colo"},{"id":56,"s":"t least 30"},{"id":84,"s":"nists in t"},{"id":34,"s":"ctrical en"},{"id":45,"s":"exist for "},{"id":92,"s":"ce rating\n"},{"id":26,"s":"must inclu"},{"id":46,"s":"several ge"}]


    В каждом элементе массива есть параметр «id» — это порядковый номер, по нему и будем сортировать кусочки сообщения. Усложним наш код:

    Предыдыщий код с усовершенствованиями:
    import base64
    import json
    import hashlib
    
    code = """W3siaWQiOjMxLCJzIjoib255OiAxIG1hbiJ9LHsiaWQiOjI3LCJzIjoiZGUgYWxsIHNwZSJ9LHsiaWQiOjYxLCJzIjoicyB3aWxsIGZhYyJ9LHsiaWQiOjU1LCJzIjoib3V0IG1lbiwgYSJ9LHsiaWQiOjQyLCJzIjoibnRpc3RzLlxuMi4ifSx7ImlkIjoxOCwicyI6Im4uIE91ciBzb2MifSx7ImlkIjo3MCwicyI6InRoZSBwbGFuZXQifSx7ImlkIjo1LCJzIjoiLiBPbmUgb3BlciJ9LHsiaWQiOjY0LCJzIjoiIGNvbmRpdGlvbiJ9LHsiaWQiOjQ0LCJzIjoieSBoYXZlIHRvICJ9LHsiaWQiOjQ5LCJzIjoic3QgNTElIG9mICJ9LHsiaWQiOjU4LCJzIjoiaXN0cyBtdXN0ICJ9LHsiaWQiOjcxLCJzIjoiLiBTZWxlY3QgcCJ9LHsiaWQiOjU5LCJzIjoiYmUgbWFsZS5cbjMifSx7ImlkIjo4NSwicyI6ImhlIGZvbGxvd2kifSx7ImlkIjo0MywicyI6IiBUaGUgY29sb24ifSx7ImlkIjo3MywicyI6IiByZXNpbGllbmMifSx7ImlkIjoyNCwicyI6InNlbGVjdGlvbi4ifSx7ImlkIjo2MiwicyI6ImUgYWxsIGtpbmQifSx7ImlkIjo0NywicyI6Im5lcmF0aW9ucywifSx7ImlkIjoxMDUsInMiOiJlb3BsZSB3aG8gIn0seyJpZCI6MTAzLCJzIjoicm1hdGlvbiwgcyJ9LHsiaWQiOjIyLCJzIjoiaWFzIHRvIHNpbSJ9LHsiaWQiOjg2LCJzIjoibmcgZm9ybWF0OiJ9LHsiaWQiOjIsInMiOiJwJ3MgbGFuZGVyIn0seyJpZCI6MzYsInMiOiIgY29tcHV0ZXIgIn0seyJpZCI6NDgsInMiOiIgc28gYXQgbGVhIn0seyJpZCI6MTYsInMiOiJmb3IgTWFycyBjIn0seyJpZCI6NzUsInMiOiIwIGFuZCBtb3JlIn0seyJpZCI6MTEsInMiOiIuIFlvdSBoYXZlIn0seyJpZCI6OTUsInMiOiJlbWFsZSA9IDAsIn0seyJpZCI6NjgsInMiOiJwbGUgc2hvdWxkIn0seyJpZCI6NDEsInMiOiJzLCAxNCBzY2llIn0seyJpZCI6NywicyI6IiwgYnV0IGlzIHUifSx7ImlkIjo5OCwicyI6ImUgcmF0aW5nXG5lIn0seyJpZCI6MCwicyI6IlR3byBvZiB0aGUifSx7ImlkIjoxMDEsInMiOiIuXG5cbktub3dpbmcifSx7ImlkIjozMywicyI6Im9rcywgNCBlbGUifSx7ImlkIjo5NywicyI6IjtSZXNpbGllbmMifSx7ImlkIjo1MSwicyI6Im9sb25pc3RzIG0ifSx7ImlkIjoxMDgsInMiOiJkIFBsYW5ldC4ifSx7ImlkIjoxMDAsInMiOiJicmVhayAtIFxcbiJ9LHsiaWQiOjYwLCJzIjoiLiBDb2xvbmlzdCJ9LHsiaWQiOjQwLCJzIjoiOCBtZWNoYW5pYyJ9LHsiaWQiOjc4LCJzIjoiMTAwLlxuXG5UaGUgIn0seyJpZCI6MSwicyI6IiB0aHJlZSBzaGkifSx7ImlkIjoxMywicyI6IjAgbW9zdCBhcHAifSx7ImlkIjoxMiwicyI6IiB0byBwaWNrIDQifSx7ImlkIjo5MywicyI6IklEO1Byb2Zlc3MifSx7ImlkIjoxMDQsInMiOiJlbGVjdCA0MCBwIn0seyJpZCI6MzksInMiOiI1IG1lZGljcywgIn0seyJpZCI6OCwicyI6Im5hYmxlIHRvIGMifSx7ImlkIjo1MiwicyI6InVzdCBiZSBmZW0ifSx7ImlkIjo5NiwicyI6IiBtYWxlID0gMSkifSx7ImlkIjo4OSwicyI6ImZlbWFsZSA9IDAifSx7ImlkIjoxMCwicyI6IiBjb2xvbmlzdHMifSx7ImlkIjo2NSwicyI6InMuIE9ubHkgdGgifSx7ImlkIjozNSwicyI6ImdpbmVlcnMsIDUifSx7ImlkIjoyMSwicyI6InJlZSBjcml0ZXIifSx7ImlkIjoxNywicyI6Im9sb25pc2F0aW8ifSx7ImlkIjo1NCwicyI6IiBmb3JnZXQgYWIifSx7ImlkIjoxNSwicyI6ImFuZGlkYXRlcyAifSx7ImlkIjo4NywicyI6IiBJRDtQcm9mZXMifSx7ImlkIjozNywicyI6InN5c3RlbSBzcGUifSx7ImlkIjo3OSwicyI6ImZpbGUgYXR0YWMifSx7ImlkIjo5MCwicyI6IiwgbWFsZSA9IDEifSx7ImlkIjoyOSwicyI6InNzZW50aWFsIGYifSx7ImlkIjoxMDcsInMiOiJpemUgdGhlIFJlIn0seyJpZCI6NiwicyI6ImF0ZXMgZnVsbHkifSx7ImlkIjo2NiwicyI6ImUgbW9zdCByZXMifSx7ImlkIjozMiwicyI6ImFnZXIsIDMgY28ifSx7ImlkIjo2MywicyI6InMgb2YgaGFyc2gifSx7ImlkIjo0LCJzIjoidW5jdGlvbmluZyJ9LHsiaWQiOjgyLCJzIjoidGFpbnMgYSBsaSJ9LHsiaWQiOjM4LCJzIjoiY2lhbGlzdHMsICJ9LHsiaWQiOjc0LCJzIjoiZSByYXRpbmcgNiJ9LHsiaWQiOjc3LCJzIjoiZnJvbSAxIHRvICJ9LHsiaWQiOjE5LCJzIjoiaW9sb2dpc3RzICJ9LHsiaWQiOjMsInMiOiJzIGFyZSBtYWxmIn0seyJpZCI6NTMsInMiOiJhbGUuIERvbid0In0seyJpZCI6MjMsInMiOiJwbGlmeSB0aGUgIn0seyJpZCI6MjgsInMiOiJjaWFsaXN0cyBlIn0seyJpZCI6ODgsInMiOiJzaW9uO1NleCAoIn0seyJpZCI6OTQsInMiOiJpb247U2V4IChmIn0seyJpZCI6MTA2LCJzIjoid2lsbCBjb2xvbiJ9LHsiaWQiOjI1LCJzIjoiXG5cbjEuIENyZXcgIn0seyJpZCI6MTAyLCJzIjoiIHRoaXMgaW5mbyJ9LHsiaWQiOjkxLCJzIjoiKTtSZXNpbGllbiJ9LHsiaWQiOjgxLCJzIjoicyB0YXNrIGNvbiJ9LHsiaWQiOjU3LCJzIjoiJSBvZiBjb2xvbiJ9LHsiaWQiOjc2LCJzIjoiIG9uIHNjYWxlICJ9LHsiaWQiOjMwLCJzIjoib3IgdGhlIGNvbCJ9LHsiaWQiOjgwLCJzIjoiaGVkIHRvIHRoaSJ9LHsiaWQiOjE0LCJzIjoicm9wcmlhdGUgYyJ9LHsiaWQiOjcyLCJzIjoiZW9wbGUgd2l0aCJ9LHsiaWQiOjY5LCJzIjoiIGNvbG9uaXplICJ9LHsiaWQiOjksInMiOiJvbnRhaW4gMTIwIn0seyJpZCI6MjAsInMiOiJkZWZpbmVkIHRoIn0seyJpZCI6NjcsInMiOiJpbGllbnQgcGVvIn0seyJpZCI6NTAsInMiOiJzZWxlY3RlZCBjIn0seyJpZCI6OTksInMiOiJ0Yy4gXG5MaW5lICJ9LHsiaWQiOjgzLCJzIjoic3Qgb2YgY29sbyJ9LHsiaWQiOjU2LCJzIjoidCBsZWFzdCAzMCJ9LHsiaWQiOjg0LCJzIjoibmlzdHMgaW4gdCJ9LHsiaWQiOjM0LCJzIjoiY3RyaWNhbCBlbiJ9LHsiaWQiOjQ1LCJzIjoiZXhpc3QgZm9yICJ9LHsiaWQiOjkyLCJzIjoiY2UgcmF0aW5nXG4ifSx7ImlkIjoyNiwicyI6Im11c3QgaW5jbHUifSx7ImlkIjo0NiwicyI6InNldmVyYWwgZ2UifV0="""
    
    def base64ToString(b):
        return base64.b64decode(b).decode('utf-8')
    
    
    def sortbylen(sortlist):
        return sortlist[ids]
    
    
    decodedJSON = base64ToString(code)
    print(decodedJSON)
    sortString = json.loads(decodedJSON)
    sortList = list()
    
    for item in sortString:
        sortList.append(item)
    
    elem = sortList[0]
    ids = list(elem.keys())[0]
    messages = list(elem.keys())[1]
    
    sortList.sort(key=sortbylen)
    finallyDecodedString = ''
    
    
    for item in sortList:
        finallyDecodedString = finallyDecodedString + '' + str(item[messages])
    print(finallyDecodedString + '\n')
    
    print('Md5 hash: ' + hashlib.md5(finallyDecodedString.encode('utf-8')).hexdigest())
    


    В итоге, получим сообщение и md5-хэш сообщения:

    Two of the three ship's landers are malfunctioning. One operates fully, but is unable to contain 120 colonists. You have to pick 40 most appropriate candidates for Mars colonisation. Our sociologists defined three criterias to simplify the selection.

    1. Crew must include all specialists essential for the colony: 1 manager, 3 cooks, 4 electrical engineers, 5 computer system specialists, 5 medics, 8 mechanics, 14 scientists.
    2. The colony have to exist for several generations, so at least 51% of selected colonists must be female. Don't forget about men, at least 30% of colonists must be male.
    3. Colonists will face all kinds of harsh conditions. Only the most resilient people should colonize the planet. Select people with resilience rating 60 and more on scale from 1 to 100.

    The file attached to this task contains a list of colonists in the following format: ID;Profession;Sex (female = 0, male = 1);Resilience rating
    ID;Profession;Sex (female = 0, male = 1);Resilience rating
    etc.
    Line break - \n.

    Knowing this information, select 40 people who will colonize the Red Planet.

    Md5 hash: 88f57ef56978a95f044183951eabef3b


    К сообщению прилагается файл с данными о колонистах в следующем формате:

    1;electrical engineer;0;90
    2;mechanic;0;77
    3;scientist;1;71


    Формат такой: ID; Профессия; Пол(женщина = 0, мужчина = 1); Рейтинг устойчивости

    Предлагается из 120 колонистов набрать 40, удовлетворяющих следующим критериям:

    1. В колонии должен быть 1 управляющий, 3 повара, 4 электрика, 5 компьютерщиков, 5 медиков, 8 механиков и 14 ученых.
    2. Колонии предстоит существовать не один год, поэтому, как минимум, 51% жителей должны быть женщинами. Но не менее 30% колонистов должны быть представителями мужского пола.
    3. В колонии жить не так просто и весело, поэтому необходимо выбрать только кандидатов, чей рейтинг устойчивости равен 60 и более единицам по шкале от 1 до 100.

    А теперь приступим к отбору колонистов!

    Отбираем...
    import random
    import math
    import sys
    
    colonists_list = list()
    new_colonists_list_male = list()
    new_colonists_list_female = list()
    manager_fem_list = list()
    manager_mal_list = list()
    cook_fem_list = list()
    cook_mal_list = list()
    elen_fem_list = list()
    elen_mal_list = list()
    compspec_fem_list = list()
    compspec_mal_list = list()
    doc_fem_list = list()
    doc_mal_list = list()
    mech_fem_list = list()
    mech_mal_list = list()
    sci_fem_list = list()
    sci_mal_list = list()
    lucky_colonists = list()
    
    luck_list = [random.randint(0, 100) for i in range(100)]
    
    managers = 1
    cooks = 3
    electrical_engineers = 4
    computer_system_specialists = 5
    medics = 5
    mechanics = 8
    scientists = 14
    
    def pick_percentage(low, high):
        return random.randint(low, high)/100
    
    perc_male = pick_percentage(30, 49)
    perc_female = pick_percentage(51, 69)
    
    quantity_required = 40
    quantity_of_women_required = math.ceil(quantity_required * perc_female)
    min_percentage_fem = quantity_of_women_required/quantity_required
    quantity_of_men_required = quantity_required * perc_male
    min_percentage_mal = quantity_of_men_required/quantity_required
    
    unsorted_list = list()
    while 1:
        try:
            line = input()
        except EOFError:
            break
            unsorted_list.append(line)
    
    sorted_list = list()
    for i in unsorted_list:
        sorted_list.append(i.split(';'))
    
    # сортировка по рейтингу
    for item in sorted_list:
        if int(item[3]) >= 60:
            colonists_list.append(item)
    
    counter = 0
    
    conditions = "1. Crew must include all specialists essential for the colony: 1 manager, 3 cooks," \
                 " 4 electrical engineers, 5 computer system specialists, 5 medics, 8 mechanics, 14 " \
                 "scientists. 2. The colony have to exist for several generations, so at least 51% " \
                 "of selected colonists must be female. Don't forget about men, at least 30% of colonists must be male."\
                 "3. Colonists will face all kinds of harsh conditions. Only the most resilient people should colonize " \
                 "the planet. Select people with resilience rating 60 and more on scale from 1 to 100."
    
    # отсортируем женщин
    for item in colonists_list:
        if int(item[2]) == 0:
            new_colonists_list_female.append(item)
    
    # отсортируем мужчин
    for item in colonists_list:
        if int(item[2]) == 1:
            new_colonists_list_male.append(item)
    
    # отсортируем по профессиям женщин
    for item in new_colonists_list_female:
        if item[1] == 'manager':
            manager_fem_list.append(item)
        if item[1] == 'cook':
            cook_fem_list.append(item)
        if item[1] == 'electrical engineer':
            elen_fem_list.append(item)
        if item[1] == 'computers specialist':
            compspec_fem_list.append(item)
        if item[1] == 'doctor':
            doc_fem_list.append(item)
        if item[1] == 'mechanic':
            mech_fem_list.append(item)
        if item[1] == 'scientist':
            sci_fem_list.append(item)
    
    
    # отсортируем по профессиям мужчин
    for item in new_colonists_list_male:
        if item[1] == 'manager':
            manager_mal_list.append(item)
        if item[1] == 'cook':
            cook_mal_list.append(item)
        if item[1] == 'electrical engineer':
            elen_mal_list.append(item)
        if item[1] == 'computers specialist':
            compspec_mal_list.append(item)
        if item[1] == 'doctor':
            doc_mal_list.append(item)
        if item[1] == 'mechanic':
            mech_mal_list.append(item)
        if item[1] == 'scientist':
            sci_mal_list.append(item)
    
    
    # Подберем колонистов
    
    
    def percentage_is_ok(colonists):
        female_quantity = 0
        female_is_ok = 1
        male_is_ok = 1
        for i in colonists:
            if int(i[2]) == 0:
                female_quantity = female_quantity + 1
        percentage_female = female_quantity/int(len(colonists))
        percentage_male = 1 - percentage_female
        if percentage_female < min_percentage_fem:
            female_is_ok = 0
        elif percentage_male < min_percentage_mal:
            male_is_ok = 0
        return [female_is_ok, male_is_ok, percentage_female, percentage_male, female_quantity]
    
    
    def choose_colonists(female, male, quantity, resulting_list):
        for i in range(quantity):
            if int(percentage_is_ok(resulting_list)[0]) == 0 and len(female) > 0:
                colonist_choice = random.choice(female)
                resulting_list.append(colonist_choice)
                female.remove(colonist_choice)
            elif int(percentage_is_ok(resulting_list)[1]) == 0 and len(male) > 0:
                colonist_choice = random.choice(male)
                resulting_list.append(colonist_choice)
                male.remove(colonist_choice)
            else:
                if len(male) > 0 and len(female) > 0:
                    sex_choice = random.choice([female, male])
                    colonist_choice = random.choice(sex_choice)
                    resulting_list.append(colonist_choice)
                    sex_choice.remove(colonist_choice)
                else:
                    if len(male) == 0:
                        colonist_choice = random.choice(female)
                        resulting_list.append(colonist_choice)
                        sex_choice.remove(colonist_choice)
                    elif len(female) == 0:
                        colonist_choice = random.choice(male)
                        resulting_list.append(colonist_choice)
                        sex_choice.remove(colonist_choice)
    
    
    # управляющих
    if int(random.choice(luck_list)) >= 50:
        choice = random.choice(manager_fem_list)
        lucky_colonists.append(choice)
    else:
        choice = random.choice(manager_mal_list)
        lucky_colonists.append(choice)
    
    # поваров
    choose_colonists(cook_fem_list, cook_mal_list, cooks, lucky_colonists)
    
    # электроинженеров
    choose_colonists(elen_fem_list, elen_mal_list, electrical_engineers, lucky_colonists)
    
    # компьютерных инженеров
    choose_colonists(compspec_fem_list, compspec_mal_list, computer_system_specialists, lucky_colonists)
    
    # медиков - частный случай
    doctors_left = 1
    for i in range(medics):
        if int(percentage_is_ok(lucky_colonists)[0]) == 0:
            choice = random.choice(doc_fem_list)
            lucky_colonists.append(choice)
            doc_fem_list.remove(choice)
        elif doctors_left > 0:
            choice = random.choice(doc_mal_list)
            lucky_colonists.append(choice)
            doc_mal_list.remove(choice)
            doctors_left = 0
        else:
            choice = random.choice(doc_fem_list)
            lucky_colonists.append(choice)
            doc_fem_list.remove(choice)
    
    # механиков - частный случай
    mechanics_male_left = 6
    for i in range(mechanics):
        if int(percentage_is_ok(lucky_colonists)[0]) == 0:
            choice = random.choice(mech_fem_list)
            lucky_colonists.append(choice)
            mech_fem_list.remove(choice)
        elif mechanics_male_left > 0:
            choice = random.choice(mech_mal_list)
            lucky_colonists.append(choice)
            mech_mal_list.remove(choice)
            mechanics_male_left = mechanics_male_left - 1
        else:
            choice = random.choice(mech_fem_list)
            lucky_colonists.append(choice)
    
    # ученых
    choose_colonists(sci_fem_list, sci_mal_list, scientists, lucky_colonists)
    
    colonists_string = ''
    
    for item in lucky_colonists:
        colonists_string = colonists_string + item[0] + ';'
    colonists_string = colonists_string[0:len(colonists_string)-1]
    
    #выведем 
    sys.stdout.write(colonists_string)
    


    Форма на сайте принимает код и автоматически его тестирует. Данные подаются из потока стандартного ввода, по этой же причине, приходится их считывать оттуда, что немного усложняет тестирование кода при написании.

    Январь 2019


    Хотел поделиться с вами этим, довольно интересным, опытом, участия в подобных квестах. Для меня он оказался первым. Мне кажется, идея интересная даже без конкурсной основы. Лично я нарешал штук 5 задач разных типов. Остальные прикладываю по ссылке на github.

    А еще, в качестве вознаграждения, в конце выпадает рейтинговая таблица с вашим временем. Для меня лучший результат оказался таким:



    Итог, символическое 50 место из более 400+ участников.

    Что можно об этом сказать? Для меня это было увлекательно проведенное за программированием время. Кстати задания все еще доступны для желающих их порешать, несмотря на то, что конкурс давно завершен.

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 7

      +2
      Даже для прохождения текстового квеста настало время (нет) ввести e-mail, телефон и согласие на обработку персональных данных для третьих лиц… Теперь я не могу даже поиграть в текстовый квест. Грустно :(
        0
        К сожалению, такое время :(
        0
        sortList.sort(key=sortbylen)

        sortbylen не определена, что я делаю не так?
          0
          Вот определение функции из моего кода:

          def sortbylen(sortlist):
              return sortlist[ids]
            0
            Нашел опечатку, спасибо.
          0
          Ничего сексисткого, но почему:
          1;electrical engineer;0;90
          2;mechanic;0;77
          3;scientist;1;71

          Формат такой: ID; Профессия; Пол(женщина = 0, мужчина = 1); Рейтинг устойчивости

          Електро-монтер и механик — женщины, а ученый — мужчина?
            +1
            Скрытый сексизм! А то, что женщина — 0, а мужчина — 1 вас не смутило?

          Only users with full accounts can post comments. Log in, please.