├── CMakeLists.txt ├── LICENSE ├── README.org └── src ├── bit-packer.h ├── compress.h ├── file-backed-array.h ├── search.h ├── snakebird ├── level00.cc ├── level01.cc ├── level02.cc ├── level03.cc ├── level04.cc ├── level05.cc ├── level06.cc ├── level07.cc ├── level08.cc ├── level09.cc ├── level10.cc ├── level11.cc ├── level12.cc ├── level13.cc ├── level14.cc ├── level15.cc ├── level16.cc ├── level17.cc ├── level18.cc ├── level19.cc ├── level20.cc ├── level21.cc ├── level22.cc ├── level23.cc ├── level24.cc ├── level25.cc ├── level26.cc ├── level27.cc ├── level28.cc ├── level29.cc ├── level30.cc ├── level31.cc ├── level32.cc ├── level33.cc ├── level34.cc ├── level35.cc ├── level36.cc ├── level37.cc ├── level38.cc ├── level39.cc ├── level40.cc ├── level41.cc ├── level42.cc ├── level43.cc ├── level44.cc ├── level45.cc ├── levelstar1.cc ├── levelstar2.cc ├── levelstar3.cc ├── levelstar4.cc ├── levelstar5.cc ├── levelstar6.cc ├── levelvoid.cc ├── main.h └── snakebird.h ├── third-party └── cityhash │ ├── city.cc │ ├── city.h │ └── citycrc.h └── util.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 4 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 5 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 6 | 7 | set(CMAKE_CXX_COMPILER "clang++") 8 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -O2 -g3 -Wall -Werror -Wno-sign-compare -march=native") 9 | 10 | include_directories("src") 11 | 12 | foreach(level 13 | 00 01 02 03 04 05 06 07 08 09 14 | 10 11 12 13 14 15 16 17 18 19 15 | 20 21 22 23 24 25 26 27 28 29 16 | 30 31 32 33 34 35 36 37 38 39 17 | 40 41 42 43 44 45 18 | star1 star2 star3 star4 star5 star6 void) 19 | add_executable(snakebird.${level} 20 | src/snakebird/level${level}.cc 21 | src/third-party/cityhash/city.cc) 22 | target_link_libraries(snakebird.${level} zstd) 23 | endforeach() 24 | 25 | find_library(zstd libstd) 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | A standard MIT license follows (code under src/third-party might 2 | have different licenses, please consult the files). 3 | 4 | Copyright (c) 2018 Juho Snellman 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | ** Snakebird Solver 2 | 3 | This repository contains a C++ breadth-first solver for the delightful 4 | puzzle game [[http://snakebird.noumenongames.com/][Snakebird]]. Theb 5 | code is licensed under the MIT license, except for files under 6 | third-party/src. For those files, see the files for licensing 7 | information. 8 | 9 | Thanks to [[https://github.com/apocalyptech/snakebirdsolver][apocalyptech/snakebirdsolver]] for the idea on how to 10 | represent the Snakes in ASCII graphics in the puzzle definitions and 11 | pretty-printing, and for the level definitions. 12 | 13 | *** Performance 14 | 15 | The solver has a very optimized internal representation for game 16 | states, and for processing the game physics. Generating all the output 17 | states for an average state in the hardest puzzles of the game takes 18 | roughly 1-2 microseconds on an i7-6700k. The bulk of the program 19 | running time is spent in determining which of the output states had 20 | already been visited. 21 | 22 | The representation of an individual state is usually about 8-10 bytes, 23 | but a data format + a combination of compression techniques compresses 24 | that down to an average of 0.7 bits. 25 | 26 | All memory access patterns are optimized with the assumption that the 27 | state will eventually spill to disk. So performance will not fall 28 | completely off the cliff even if the working set does not fit full in 29 | RAM. But the practical RAM requirements are actually pretty small even 30 | for the larger puzzles. 31 | 32 | To keep the state state manageable, the states are canonicalized in 33 | a way that eliminates as many symmetries as possible. In particular, 34 | the identity of objects or snakes is not tracked across states; the 35 | only things that matter are their shapes and locations. This 36 | canonicalization can have a dramatic effect on some puzzles. 37 | 38 | For example finding the solution to the star4 puzzle requires visiting 39 | about 36M states in its original state, but only 3.3M states after 40 | canonicalization. 41 | -------------------------------------------------------------------------------- /src/bit-packer.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++ -*- 2 | 3 | #ifndef BIT_PACKER_H 4 | #define BIT_PACKER_H 5 | 6 | #include "util.h" 7 | 8 | // A class for serializing/deserializing a stream of variable 9 | // width fields (max _Bits_ bits in total) into a byte stream 10 | // of fixed length. 11 | // 12 | // Serialization example: 13 | // 14 | // int a = 42, b = 31; 15 | // Packer<63> data; 16 | // Packer::Context pack; 17 | // data.deposit(a, 6, &pack); 18 | // data.deposit(b, 7, &pack); 19 | // memcpy(out, data.bytes_, data::Bytes); 20 | // 21 | // Deserialization example: 22 | // 23 | // int a = 42, b = 31; 24 | // Packer<63> data; 25 | // memcpy(data.bytes_, in, data::Bytes); 26 | // Packer::Context unpack; 27 | // data.extract(a, 6, &unpack); 28 | // data.extract(b, 7, &unpack); 29 | template 30 | struct Packer { 31 | // Size of the serialized data. 32 | static const int Bytes = (Bits + 7) / 8; 33 | 34 | // The serialized data. Should be initialized to contain the 35 | // appropriate bytes when deserializing. When serializing, 36 | // will contain the serialized byte stream after a call to 37 | // flush(). 38 | uint8_t bytes_[Bytes] = { 0 }; 39 | 40 | // Serialization / deserialization context. Should be considered 41 | // opaque internal data. 42 | struct Context { 43 | // Data that's been read from the backing buffer but not 44 | // yet returned by extract(), or data that's been written 45 | // with deposit() but not yet flushed. 46 | uint64_t acc_ = 0; 47 | // How many bits of acc_ are valid. 48 | size_t acc_bits_ = 0; 49 | // The bytes that have been fully read / written from the 50 | // backing buffer. 51 | size_t at_ = 0; 52 | }; 53 | 54 | // Store the lowest _width_ bits from _data_ into the appropriate 55 | // bits of the buffer. Might cause a flush of the buffer into 56 | // the backing array. 57 | template 58 | void deposit(T data, size_t width, Context* context) { 59 | assert(width <= 64); 60 | if (context->acc_bits_ + width > 64) { 61 | flush(context); 62 | } 63 | context->acc_ |= (uint64_t) data << context->acc_bits_; 64 | context->acc_bits_ += width; 65 | } 66 | 67 | // Ensures all deposited data is written from the buffer to the 68 | // backing array. 69 | void flush(Context* context) { 70 | uint64_t data = context->acc_; 71 | uint64_t width = context->acc_bits_; 72 | size_t at = context->at_; 73 | 74 | while (width) { 75 | size_t offset = (at % 8); 76 | int bits_to_deposit = std::min(width, 8 - offset); 77 | int deposit_at = at / 8; 78 | bytes_[deposit_at] |= data << offset; 79 | data >>= bits_to_deposit; 80 | at += bits_to_deposit; 81 | width -= bits_to_deposit; 82 | } 83 | 84 | context->acc_ = 0; 85 | context->acc_bits_ = 0; 86 | context->at_ = at; 87 | } 88 | 89 | // Reads the next _width_ bits from the backing array, and 90 | // stores them in _data_. 91 | template 92 | void extract(T& data, size_t width, Context* context) const { 93 | if (context->acc_bits_ < width) { 94 | refill(context); 95 | } 96 | data = context->acc_ & mask_n_bits(width); 97 | context->acc_ >>= width; 98 | context->acc_bits_ -= width; 99 | } 100 | 101 | private: 102 | // Fill acc_ from bytes_. (Only reads full bytes; this makes 56 103 | // bits the maximum field size that's guaranteed to work.). 104 | void refill(Context* context) const { 105 | while (context->acc_bits_ <= 56 && 106 | context->at_ < Bytes) { 107 | context->acc_ |= 108 | (uint64_t) bytes_[context->at_++] << context->acc_bits_; 109 | context->acc_bits_ += 8; 110 | } 111 | } 112 | }; 113 | 114 | #endif // BIT_PACKER_H 115 | -------------------------------------------------------------------------------- /src/compress.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++ -*- 2 | 3 | #ifndef COMPRESS_H 4 | #define COMPRESS_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | 13 | // An encoder / decoder for variable-width integers of at most _Width_ 14 | // bits. Uses the classic stop-bit approach, where the 7 low bits have 15 | // payload, the top bit being zero means this is the last byte for 16 | // this value. The last possible byte for a given width will also use 17 | // the top bit for payload. (E.g. a VarInt width of 8 will always 18 | // require exactly one byte). 19 | // 20 | // Only unsigned integers are supported. 21 | template 22 | class VarInt { 23 | public: 24 | static const uint64_t kTopBit = 1 << 7; 25 | 26 | // Reads an integer from the octet pointer _it_. Advances the 27 | // pointer, returns the integer. 28 | static uint64_t decode(const uint8_t*& it) { 29 | uint8_t byte = *it++; 30 | if (Width <= 8) { 31 | return byte; 32 | } 33 | if (Width <= 15) { 34 | if (!(byte & kTopBit)) { 35 | return byte; 36 | } 37 | return (byte & 0x7f) | (*it++ << 7); 38 | } 39 | if (Width <= 22) { 40 | if (!(byte & kTopBit)) { 41 | return byte; 42 | } 43 | uint64_t val = byte & ~kTopBit; 44 | byte = *it++; 45 | if (!(byte & kTopBit)) { 46 | return val | byte << 7; 47 | } else { 48 | val |= (byte & ~kTopBit) << 7; 49 | } 50 | byte = *it++; 51 | val |= byte << 14; 52 | return val; 53 | } 54 | assert(false); 55 | } 56 | 57 | // Encodes _value_. Calls _emit_ once for each output byte. 58 | static void encode(uint64_t value, std::function emit) { 59 | if (Width <= 8) { 60 | emit(value); 61 | return; 62 | } 63 | if (Width <= 15) { 64 | if (value <= mask_n_bits(7)) { 65 | emit(value); 66 | return; 67 | } 68 | emit((value & mask_n_bits(7)) | kTopBit); 69 | emit(value >> 7); 70 | return; 71 | } 72 | if (Width <= 22) { 73 | if (value <= mask_n_bits(7)) { 74 | emit(value); 75 | return; 76 | } 77 | if (value <= mask_n_bits(14)) { 78 | emit((value & mask_n_bits(7)) | kTopBit); 79 | emit((value >> 7) & mask_n_bits(7)); 80 | return; 81 | } 82 | emit((value & mask_n_bits(7)) | kTopBit); 83 | emit(((value >> 7) & mask_n_bits(7)) | kTopBit); 84 | emit(value >> 14); 85 | return; 86 | } 87 | assert(false); 88 | } 89 | }; 90 | 91 | // The following classes deal with compression and decompression of 92 | // streams of (preferably sorted) fixed-size records. 93 | // 94 | // There are two layers of compression going on. The inner layer 95 | // is a radix delta transformation, where a record is encoded as: 96 | // 97 | // 98 | // 99 | // If bit X is not set in the leading VarInt, the Xth octet in the 100 | // record is the same as in the previous record in the stream. The 101 | // VarInt will have all bits set for the first record. 102 | // 103 | // The optional outer layer is normal zstd compression, with blocks 104 | // that correspond to roughly 32k of plaintext. Each block is 105 | // preceded by the compressed length of the block encoded as a VarInt. 106 | 107 | 108 | // Decompresses records of _Length_ bytes from an octet buffer 109 | // that's encoded in the format described above. If _Compress_ 110 | // is false, only applies the radix delta transform. 111 | template 112 | class ByteArrayDeltaDecompressor { 113 | public: 114 | ByteArrayDeltaDecompressor(const uint8_t* begin, const uint8_t* end) 115 | : raw_it_(begin), 116 | raw_end_(end) { 117 | if (Compress) { 118 | refill(); 119 | } else { 120 | it_ = begin; 121 | end_ = end; 122 | } 123 | } 124 | 125 | // Reads a record from the buffer, and stores it in _value_. 126 | // Unless this is the first call to unpack(), _value_ should 127 | // contain bytes that are equal to the previous record. 128 | // 129 | // Returns false if all the records have been read already. 130 | bool unpack(uint8_t value[Length]) { 131 | if (it_ == end_ && !refill()) { 132 | return false; 133 | } 134 | unpack_internal(value); 135 | 136 | return true; 137 | } 138 | 139 | private: 140 | ByteArrayDeltaDecompressor( 141 | const ByteArrayDeltaDecompressor& other) = delete; 142 | ByteArrayDeltaDecompressor& operator=( 143 | const ByteArrayDeltaDecompressor& other) = delete; 144 | 145 | // If the outer compression layer is in use, decompresses a block 146 | // and points the radix transformer at the uncompressed data. 147 | // We expect that records will never end up straddling two 148 | // blocks. 149 | bool refill() { 150 | if (!Compress || 151 | raw_it_ == raw_end_) { 152 | return false; 153 | } 154 | 155 | uint64_t len = VarInt<22>::decode(raw_it_); 156 | assert(raw_it_ + len <= raw_end_); 157 | size_t zsize = ZSTD_getFrameContentSize(raw_it_, 158 | std::distance(raw_it_, 159 | raw_end_)); 160 | if (zbuffer_.size() < zsize) { 161 | zbuffer_.resize(zsize); 162 | } 163 | ZSTD_decompress(&zbuffer_[0], 164 | zsize, 165 | raw_it_, 166 | std::distance(raw_it_, raw_end_)); 167 | it_ = &zbuffer_[0]; 168 | end_ = it_ + zsize; 169 | raw_it_ += len; 170 | return true; 171 | } 172 | 173 | void unpack_internal(uint8_t output[Length]) { 174 | uint64_t n = VarInt::decode(it_); 175 | while (n) { 176 | uint64_t mask = n & -n; 177 | int bit = __builtin_ctzl(mask); 178 | output[bit] = *it_++; 179 | n ^= mask; 180 | } 181 | } 182 | 183 | // The block of data from it_ to end_ contains the delta 184 | // transformed records that unpack() will return. 185 | const uint8_t* it_; 186 | const uint8_t* end_; 187 | // The block of data from raw_it_ to raw_end_ contains data that 188 | // needs to be decompressed with zstd_. 189 | const uint8_t* raw_it_; 190 | const uint8_t* raw_end_; 191 | // A buffer for the uncompressed data. 192 | std::vector zbuffer_; 193 | }; 194 | 195 | 196 | // Compresses records of _Length_ bytes to _output_, using the format 197 | // described above. If _Compress_ is false, only applies the radix 198 | // delta transform. 199 | template 200 | class ByteArrayDeltaCompressor { 201 | public: 202 | ByteArrayDeltaCompressor(Output* output) : output_(output) { 203 | } 204 | 205 | ~ByteArrayDeltaCompressor() { 206 | flush(); 207 | } 208 | 209 | void pack(const uint8_t value[Length]) { 210 | // Compute a bitmask with bit N set if byte N is different 211 | // between "value" and the "value" given on the previous 212 | // call. 213 | uint64_t n = 0; 214 | for (int j = 0; j < Length; ++j) { 215 | if (prev_[j] != value[j]) { 216 | n |= 1 << j; 217 | } 218 | } 219 | 220 | VarInt::encode(n, 221 | [this] (uint8_t value) { 222 | record(value); 223 | }); 224 | // Emit only the changed bytes. 225 | for (int j = 0; j < Length; ++j) { 226 | if (n & (1 << j)) { 227 | // It's tempting to compute some kind of numeric 228 | // delta here, either with xor or plus/minus. Seems 229 | // like that should in theory be easier to compress 230 | // with the later passes. But it didn't work for me 231 | // in practice. 232 | record(value[j]); 233 | prev_[j] = value[j]; 234 | } 235 | } 236 | 237 | if (delta_transformed_.size() > (1 << 20)) { 238 | flush(); 239 | } 240 | } 241 | 242 | void flush() { 243 | if (Compress) { 244 | compress_and_flush(); 245 | } else { 246 | output_->insert_back(delta_transformed_.begin(), 247 | delta_transformed_.end()); 248 | delta_transformed_.clear(); 249 | } 250 | } 251 | 252 | private: 253 | ByteArrayDeltaCompressor(const ByteArrayDeltaCompressor& other) = delete; 254 | ByteArrayDeltaCompressor& operator=( 255 | const ByteArrayDeltaCompressor& other) = delete; 256 | 257 | // Buffer a byte for possible zstd compression. 258 | void record(uint8_t byte) { 259 | delta_transformed_.push_back(byte); 260 | } 261 | 262 | // Compress the internal accumulator buffer with zstd and write to 263 | // the result to the output. The compressed data will be prefixed 264 | // with a Varint representation of the length of the compressed 265 | // block. 266 | void compress_and_flush() { 267 | char buffer[1 << 22]; 268 | size_t len = ZSTD_compress(buffer, sizeof(buffer), 269 | &delta_transformed_[0], 270 | delta_transformed_.size(), 271 | 0); 272 | VarInt<22>::encode(len, [this] (uint8_t byte) { 273 | output_->push_back(byte); 274 | }); 275 | output_->insert_back(&buffer[0], &buffer[len]); 276 | delta_transformed_.clear(); 277 | } 278 | 279 | uint8_t prev_[Length] = { 0 }; 280 | std::vector delta_transformed_; 281 | Output* output_; 282 | }; 283 | 284 | // Given a byte range that's compressed/encoded as above, converts it 285 | // the range to a lazy stream of records of type T. 286 | template 287 | class StructureDeltaDecompressorStream { 288 | public: 289 | // Does not take ownership of the range. 290 | StructureDeltaDecompressorStream(const uint8_t* begin, 291 | const uint8_t* end) 292 | : stream_(begin, end) { 293 | } 294 | 295 | // Returns a reference to the latest decoded record (may not be 296 | // called if no records have been read yet). Valid only until next 297 | // call to next(). 298 | const T& value() const { 299 | return value_; 300 | } 301 | 302 | bool empty() { 303 | return empty_; 304 | } 305 | 306 | // Reads a record from the input byte array. Returns false iff 307 | // no more records can be read. Once this returns false, the only 308 | // method that may be called on this object is empty(). 309 | bool next() { 310 | if (!stream_.unpack(value_.bytes())) { 311 | empty_ = true; 312 | } 313 | return !empty_; 314 | } 315 | 316 | bool operator<(const StructureDeltaDecompressorStream& other) const { 317 | return value_ < other.value_; 318 | } 319 | 320 | private: 321 | StructureDeltaDecompressorStream(const StructureDeltaDecompressorStream& other) = delete; 322 | StructureDeltaDecompressorStream& operator=( 323 | const StructureDeltaDecompressorStream& other) = delete; 324 | 325 | T value_; 326 | bool empty_ = false; 327 | ByteArrayDeltaDecompressor stream_; 328 | }; 329 | 330 | 331 | #endif // COMPRESS_H 332 | -------------------------------------------------------------------------------- /src/file-backed-array.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++ -*- 2 | 3 | #ifndef FILE_BACKED_ARRAY_H 4 | #define FILE_BACKED_ARRAY_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | // A roughly vector-like class which is to start with stored in 20 | // normal memory. But if the total size of the array grows to 21 | // more than kFlushThreshold bytes, starts instead storing 22 | // the bulk of the data on disk (with access to the data 23 | // provided with mmap). 24 | template 27 | class file_backed_mmap_array { 28 | public: 29 | file_backed_mmap_array() { 30 | freeze(); 31 | } 32 | 33 | file_backed_mmap_array(const file_backed_mmap_array& other) = delete; 34 | 35 | file_backed_mmap_array(file_backed_mmap_array&& other) 36 | : buffer_(std::move(other.buffer_)), 37 | frozen_(other.frozen_), 38 | size_(other.size_), 39 | fd_(other.fd_), 40 | array_(other.array_) { 41 | other.array_ = NULL; 42 | other.fd_ = -1; 43 | } 44 | 45 | void operator=(file_backed_mmap_array&& other) { 46 | maybe_close(); 47 | buffer_ = std::move(other.buffer_); 48 | frozen_ = other.frozen_; 49 | size_ = other.size_; 50 | fd_ = other.fd_; 51 | array_ = other.array_; 52 | other.array_ = NULL; 53 | other.fd_ = -1; 54 | } 55 | 56 | ~file_backed_mmap_array() { 57 | maybe_close(); 58 | } 59 | 60 | // If the total size of the data structure exceeds the 61 | // flush threshold, writes whatever data we have buffered 62 | // in memory to disk. 63 | void flush() { 64 | if (buffer_.size() && fd_ >= 0) { 65 | size_t bytes = sizeof(T) * buffer_.size(); 66 | assert(write(fd_, (char*) &buffer_[0], bytes) == bytes); 67 | buffer_.clear(); 68 | } 69 | } 70 | 71 | // Returns true iff the is storing no elements. 72 | bool empty() const { return size_ == 0; } 73 | 74 | // Iterators. 75 | T* begin() { 76 | assert(frozen_); 77 | return array_; 78 | } 79 | T* end() { 80 | assert(frozen_); 81 | return array_ + size_; 82 | } 83 | 84 | const T* begin() const { 85 | assert(frozen_); 86 | return array_; 87 | } 88 | const T* end() const { 89 | assert(frozen_); 90 | return array_ + size_; 91 | } 92 | 93 | T& operator[](size_t index) { 94 | assert(frozen_); 95 | return array_[index]; 96 | } 97 | 98 | const T& operator[](size_t index) const { 99 | assert(frozen_); 100 | return array_[index]; 101 | } 102 | 103 | // Returns the number of elements in the array. 104 | size_t size() const { return size_; } 105 | 106 | // Copies "data" into the last element of the array. 107 | void push_back(const T& data) { 108 | assert(!frozen_); 109 | buffer_.push_back(data); 110 | maybe_flush(); 111 | size_++; 112 | } 113 | 114 | // Inserts a range of objects from begin to at the end 115 | // of the array. 116 | template 117 | void insert_back(const It begin, const It end) { 118 | buffer_.insert(buffer_.end(), begin, end); 119 | size_ += std::distance(begin, end); 120 | maybe_flush(); 121 | } 122 | 123 | // Prepares the array for reading. No mutating operations 124 | // may be excecuted while the array is frozen. 125 | void freeze() { 126 | maybe_map(PROT_READ, MAP_SHARED); 127 | } 128 | 129 | // Prepares the array for writing. No operations that read 130 | // specific elements may be excecuted while the array is thawed; 131 | // only pure writes. 132 | void thaw() { 133 | assert(frozen_); 134 | frozen_ = false; 135 | maybe_unmap(false); 136 | } 137 | 138 | // Empties an array. 139 | void reset() { 140 | assert(frozen_); 141 | frozen_ = false; 142 | if (fd_ >= 0) { 143 | lseek(fd_, 0, SEEK_SET); 144 | } 145 | maybe_unmap(true); 146 | size_ = 0; 147 | buffer_.clear(); 148 | run_starts_.clear(); 149 | run_ends_.clear(); 150 | } 151 | 152 | // Marks the start of a new run of elements, after the last 153 | // element currently in the array. 154 | void start_run() { 155 | run_starts_.push_back(size_); 156 | } 157 | 158 | // Marks the current run as ending after the last element 159 | // currently in the array. 160 | void end_run() { 161 | run_ends_.push_back(size_); 162 | } 163 | 164 | // Returns a range for the i'th run that was recorded for the 165 | // array. 166 | std::pair run(int i) const { 167 | return std::make_pair(run_starts_[i] + array_, 168 | run_ends_[i] + array_); 169 | } 170 | 171 | // Returns a vector of all the runs that have been recorded 172 | // for the array. 173 | using Run = std::pair; 174 | std::vector runs() const { 175 | std::vector ret; 176 | for (int i = 0; i < run_count(); ++i) { 177 | ret.push_back(std::make_pair(begin() + run_starts_[i], 178 | begin() + run_ends_[i])); 179 | } 180 | return ret; 181 | } 182 | 183 | // Returns the number of recorded runs. 184 | int run_count() const { 185 | return run_ends_.size(); 186 | } 187 | 188 | struct WriteRun { 189 | WriteRun(file_backed_mmap_array* array) : array_(array) { 190 | array_->thaw(); 191 | array_->start_run(); 192 | } 193 | 194 | ~WriteRun() { 195 | array_->end_run(); 196 | array_->freeze(); 197 | } 198 | 199 | private: 200 | file_backed_mmap_array* array_; 201 | }; 202 | 203 | private: 204 | // If the array has a backing file, unmaps it. If truncate 205 | // is additionally truncates the file. 206 | void maybe_unmap(bool truncate) { 207 | if (fd_ >= 0 && array_) { 208 | munmap((void*) array_, size_ * sizeof(T)); 209 | if (truncate) { 210 | ftruncate(fd_, 0); 211 | } 212 | } 213 | array_ = NULL; 214 | } 215 | 216 | // If the array has a backing file, unmaps and closes the file. 217 | void maybe_close() { 218 | if (fd_ >= 0) { 219 | maybe_unmap(true); 220 | close(fd_); 221 | fd_ = -1; 222 | } 223 | } 224 | 225 | // Opens a backing file for this array in the current working 226 | // directory. 227 | void open() { 228 | assert(fd_ == -1); 229 | char* fname = strdup("file-backed-tmp-XXXXXX"); 230 | fd_ = mkstemp(fname); 231 | assert(fd_ >= 0); 232 | unlink(fname); 233 | free(fname); 234 | } 235 | 236 | // Flushes the array to disk, if the array is large enough. 237 | void maybe_flush() { 238 | if (buffer_.size() >= kFlushThreshold) { 239 | if (fd_ == -1) { 240 | open(); 241 | } 242 | flush(); 243 | buffer_.clear(); 244 | } 245 | } 246 | 247 | // Freezes the array and sets array_ to point to the backing 248 | // element storage. Either an mmaped view of the backing file, or 249 | // a pointer to the start of the backing buffer. 250 | void maybe_map(int prot, int flags) { 251 | assert(!frozen_); 252 | if (fd_ >= 0 && size_ > 0) { 253 | flush(); 254 | size_t len = sizeof(T) * size_; 255 | void* map = mmap(NULL, len, prot, flags, fd_, 0); 256 | if (map == MAP_FAILED) { 257 | perror("mmap"); 258 | abort(); 259 | } 260 | array_ = (T*) map; 261 | } else { 262 | array_ = &buffer_[0]; 263 | } 264 | frozen_ = true; 265 | } 266 | 267 | std::vector run_starts_; 268 | std::vector run_ends_; 269 | // Elements that have been written to end of the array, but 270 | // not flushed to disk. 271 | std::vector buffer_; 272 | bool frozen_ = false; 273 | // The number of elements in the array. 274 | size_t size_ = 0; 275 | // The file descriptor of the backing file; -1 if the array 276 | // does not yet have a backing file. 277 | int fd_ = -1; 278 | // A pointer to the start of the backing store (whether a mmaped 279 | // view of the backing file, or the in-memory buffer). 280 | T* array_ = NULL; 281 | }; 282 | 283 | #endif // FILE_BACKED_ARRAY_H 284 | -------------------------------------------------------------------------------- /src/search.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++ -*- 2 | // 3 | // A breadth-first search optimized for secondary storage. 4 | // 5 | // This is not exactly a textbook implementation of a BFS. Instead 6 | // checking each output state against a hash table (or similar 7 | // structure) of previously seen states, this code processes the 8 | // whole depth in one go and collects up all the output states. 9 | // The set difference of these new states and the previously seen 10 | // states is then computed as a batch operation, producing the 11 | // states to use as input for the next depth. 12 | // 13 | // The main reason for doing the deduplication in a large batch is to 14 | // keep the memory footprint down. All data processing happens in a 15 | // streaming manner, there are essentially no random accesses at 16 | // all. This allows for keeping the state on disk when its size 17 | // exceeds the available physical memory, without totally destroying 18 | // performance. In addition the streaming access pattern + the 19 | // specific data structure used allows for compressing the data at 20 | // rest with very good compression ratios (10x), with decompression 21 | // happening as the data is streamed for reading. 22 | // 23 | // Despite memory footprint being the main reason for this variant 24 | // of the algorithm, it appears to be faster than the standard 25 | // hash table based variants on problems of non-trivial size, even 26 | // if all the data fits into memory. This is most likely due to 27 | // hash tables being very cache unfriendly, while the access pattern 28 | // used here is very easy for the hardware prefetcher to work with. 29 | // 30 | // Let's call a sorted sequence of states a run. 31 | // 32 | // - For each state generated at previous depth ("todo run"), generate 33 | // all outputs. Collect them in a vector of new states. 34 | // - Sort + deduplicate the new states, generating a run. 35 | // - If the number of output states grows too large, generate 36 | // multiple runs. 37 | // - Iterate through the all the "new state" runs and the run 38 | // of "seen states" in lockstep. 39 | // - If this iteration ever sees a state that's present in the 40 | // new runs but not in the old one, collect that state into the 41 | // new todo set. Since the runs are sorted, this is effectively 42 | // a set difference operation. 43 | // - Add a new run to the collection of old states, consisting of 44 | // the states kept in the last step. 45 | // - Add all distinct states to a new "seen states" run. 46 | // - Replace the old todo run and old seen states run with the outputs 47 | // of the merge. 48 | 49 | #ifndef SEARCH_H 50 | #define SEARCH_H 51 | 52 | #include 53 | #include 54 | 55 | #include "compress.h" 56 | #include "file-backed-array.h" 57 | 58 | // A default policy class, with hook implementations that do nothing. 59 | template 60 | struct BFSPolicy { 61 | // Called at the start of each new depth of the breadth-first 62 | // search. 63 | static void start_iteration(int depth) { 64 | } 65 | 66 | // Called for every state on the solution that was found. 67 | static void trace(const FixedState& setup, const State& state, int depth) { 68 | } 69 | }; 70 | 71 | // A breadth first search driven by the template parameters. 72 | // 73 | // Template parameters. 74 | // 75 | // State: A node in the state graph. Must implement: 76 | // - do_valid_moves(const FixedState& setup, 77 | // std::function fun) const 78 | // Calls fun with all states that can be reached from this 79 | // state. 80 | // - win(): Returns true if the state is in a win condition. 81 | // - print(const FixedState& setup): Prints the state to stdout. 82 | // - Must have a default constructor, which must represent a state 83 | // that is not reachable from any other state. 84 | // 85 | // FixedState: An opaque scenario description, containing data that's 86 | // needed for interpreting the State objects but that's identical 87 | // between all states. A single FixedState object will be passed to 88 | // the main entry point, and threaded through all computations. 89 | // 90 | // Policy: Hook functions called at various point of the search 91 | // process. See BFSPolicy for the set of hooks that should be 92 | // defined. 93 | // 94 | // PackedState: A byte serialization of State. 95 | // - width_bytes(): Returns the maximum possible width of the 96 | // serialization, for any possible state. 97 | // - bytes(): Returns a mutable array of width_bytes() bytes. 98 | // - hash(): Returns a hash code. 99 | // - operator< and operator==: PackedStates must have a total order. 100 | // - There must be mutual constructors from PackedState to State 101 | // and vice versa. 102 | template, 104 | class PackedState = typename State::Packed, 105 | bool Compress = true> 106 | class BreadthFirstSearch { 107 | public: 108 | // The serialized states are the main key type for our data structures. 109 | using Key = PackedState; 110 | // The data associated with a Key (generally the hash-code of the 111 | // parent state that generated the state). 112 | using Value = uint8_t; 113 | using st_pair = std::pair; 114 | 115 | // A sequence of serialized states. (Note that each state is 116 | // likely to serialize to multiple bytes, so a single element of 117 | // this array represents just a part of the state). 118 | using Keys = file_backed_mmap_array; 119 | // A sequence of values bound to states. It's expected that there 120 | // is exactly one value per key, and that the values and keys are 121 | // in the same order. 122 | using Values = file_backed_mmap_array; 123 | // The keys / values collected during a single iteration of the 124 | // search. 125 | using NewStates = std::vector; 126 | // A byte range point to a Keys array, indicating the start/end of 127 | // a sorted sequence of keys. 128 | using KeyRun = Keys::Run; 129 | 130 | using KeyStream = StructureDeltaDecompressorStream; 131 | using ValueStream = PointerStream; 132 | using KeyCompressor = ByteArrayDeltaCompressor; 135 | 136 | // Execute a search from start_state to any win state. 137 | int search(State start_state, const FixedState& setup) { 138 | State null_state; 139 | 140 | // BFS state 141 | 142 | // The unique states that were generated at some earlier 143 | // depth. Each state will be in keys_by_depth just once. The 144 | // new states generated from depth N in the search will be 145 | // in the Nth sorted run in the array. 146 | Keys keys_by_depth; 147 | // The values associated to the states in keys_by_depth, in 148 | // the same order. 149 | Values values_by_depth; 150 | // The same keys as in all_keys, but in a single sorted run. 151 | // This array gets recreated on every depth. 152 | Keys all_keys; 153 | // The winning state, if one has been found. 154 | st_pair win_state { null_state, 0 }; 155 | st_pair start_st { start_state, 0 }; 156 | 157 | // Initialize the data structures with the start state. 158 | { 159 | Keys::WriteRun key_writer { &keys_by_depth }; 160 | Values::WriteRun value_writer { &values_by_depth }; 161 | 162 | KeyCompressor compress { &keys_by_depth }; 163 | compress.pack(start_st.first.bytes()); 164 | values_by_depth.push_back(0); 165 | } 166 | 167 | { 168 | Keys::WriteRun key_writer { &all_keys }; 169 | KeyCompressor compress { &all_keys }; 170 | compress.pack(start_st.first.bytes()); 171 | } 172 | 173 | for (int iter = 0; ; ++iter) { 174 | Policy::start_iteration(iter); 175 | 176 | // The new states generated on this iteration, in some 177 | // number of sorted runs. 178 | Keys new_keys; 179 | // The values associated with the new states, in the same 180 | // order as new_keys. 181 | Values new_values; 182 | 183 | // The latest run in keys_by_depth will contain all the 184 | // states we know about but have not yet visited. 185 | auto last_run = keys_by_depth.run(keys_by_depth.run_count() - 1); 186 | if (last_run.first == last_run.second) { 187 | // We haven't won, and have no moves to process. 188 | return 0; 189 | } 190 | 191 | bool win = visit_states(setup, last_run, &new_keys, &new_values, 192 | &win_state); 193 | 194 | printf(" new states: %ld\n", new_values.size()); 195 | fflush(stdout); 196 | 197 | // Find all the new states that had never been generated 198 | // at an earlier depth. These states will be written out 199 | // to keys as a new run. All other new states will be 200 | // discarded. 201 | int uniq = dedup(&keys_by_depth, &values_by_depth, &all_keys, 202 | new_keys, new_values); 203 | printf(" new unique: %d\n", uniq); 204 | printf(" total size: %ld / %ld\n", all_keys.size(), 205 | values_by_depth.size()); 206 | 207 | if (win) { 208 | break; 209 | } 210 | } 211 | 212 | return trace_solution_path(setup, keys_by_depth, values_by_depth, win_state); 213 | } 214 | 215 | 216 | private: 217 | 218 | // Visits all states in run. Writes the generated states into 219 | // one or more runs of new_keys and new_values. If a winning 220 | // state is found, sets it to win_state and returns true. 221 | bool visit_states(const FixedState& setup, const KeyRun& run, 222 | Keys* new_keys, Values* new_values, 223 | st_pair* win_state) { 224 | // A temporary collection of new states / values. Will 225 | // get flushed into new_keys / new_values either when 226 | // it grows too large or once we've dealt with the whole 227 | // todo queue. 228 | NewStates new_states; 229 | bool win = false; 230 | 231 | // Visit all the states added on the last depth. 232 | for (KeyStream todo(run.first, run.second); todo.next(); ) { 233 | State st(todo.value()); 234 | auto parent_hash = todo.value().hash(); 235 | 236 | // For each state collect the possible output states. 237 | st.do_valid_moves(setup, 238 | [&new_states, &parent_hash, &win_state, 239 | &win] 240 | (State new_state) { 241 | st_pair pair(new_state, 242 | parent_hash & 0xff); 243 | new_states.push_back(pair); 244 | if (new_state.win()) { 245 | *win_state = pair; 246 | win = true; 247 | return true; 248 | } 249 | return false; 250 | }); 251 | // If we collect too many new states, do an 252 | // intermediate deduplication + compression step now. 253 | if (new_states.size() > 100000000) { 254 | pack_pairs(&new_states, new_keys, new_values); 255 | } 256 | } 257 | // Dedup + compression any leftovers. 258 | pack_pairs(&new_states, new_keys, new_values); 259 | 260 | return win; 261 | } 262 | 263 | // Works backwards from the winning state to the start state, 264 | // calling Policy::trace on each state. Note that this function 265 | // takes advantage of the original keys_by_depth having one run per 266 | // depth; it should not be called with compacted_keys_by_depth. 267 | int trace_solution_path(const FixedState& setup, 268 | const Keys& keys_by_depth, 269 | const Values& values_by_depth, 270 | const st_pair win_state) { 271 | st_pair target = win_state; 272 | 273 | int depth = keys_by_depth.run_count(); 274 | 275 | for (int i = depth - 1; i > 0; --i) { 276 | Policy::trace(setup, State(target.first), i); 277 | 278 | auto runinfo = keys_by_depth.run(i - 1); 279 | // Work through all the states at a given depth. 280 | KeyStream stream(runinfo.first, runinfo.second); 281 | 282 | bool found_next = false; 283 | for (int j = 0; stream.next(); ++j) { 284 | const auto& key = stream.value(); 285 | st_pair current(key, 0); 286 | 287 | // Look for states whose hash matches the value of 288 | // the current states. (Since we've stored the hash 289 | // of each state's parent in the value). This allows 290 | // us to skip the full expensive test for all but 291 | // one in 256 states. 292 | if ((key.hash() & 0xff) != (target.second & 0xff)) { 293 | continue; 294 | } 295 | 296 | // If the hashes matched, this is a potential parent 297 | // of the current state. Try generating the output 298 | // states for the potential parent, and check if any 299 | // of them match the current one. 300 | State st(key); 301 | if (st.do_valid_moves(setup, 302 | [&target](State new_state) { 303 | Key p(new_state); 304 | if (p == target.first) { 305 | return true; 306 | } 307 | return false; 308 | })) { 309 | // Got a match; set the potential parent as the 310 | // current state. 311 | const auto& value = values_by_depth.run(i - 1).first[j]; 312 | target = st_pair(key, value); 313 | found_next = true; 314 | break; 315 | } 316 | } 317 | assert(found_next); 318 | } 319 | Policy::trace(setup, State(target.first), 0); 320 | 321 | return depth - 1; 322 | } 323 | 324 | // Given a vector of newly generated states+value pairs, 325 | // deduplicates the states against other states in the same 326 | // vector. If there are multiple pairs with identical states 327 | // but different values, keeps an arbitrary pair. 328 | // 329 | // Writes the states (in sorted order) to new_keys. 330 | // Writes the values (in the same order as the states) to new_values. 331 | void pack_pairs(NewStates* new_states, Keys* new_keys, 332 | Values* new_values) { 333 | std::sort(new_states->begin(), new_states->end(), 334 | [] (const st_pair& a, const st_pair& b) { 335 | return a.first < b.first; 336 | }); 337 | Key prev; 338 | 339 | Keys::WriteRun key_writer { new_keys }; 340 | Values::WriteRun value_writer { new_values }; 341 | KeyCompressor compress { new_keys }; 342 | for (const auto& pair : *new_states) { 343 | if (pair.first == prev) { 344 | continue; 345 | } 346 | compress.pack(pair.first.bytes()); 347 | new_values->push_back(pair.second); 348 | prev = pair.first; 349 | } 350 | 351 | new_states->clear(); 352 | } 353 | 354 | // Finds all states in new_keys that are not present in 355 | // all_keys. Adds them to keys_by_depth as a new run. Rewrites 356 | // all_keys to be a single run that's a union of new_keys 357 | // and the original all_keys. 358 | // 359 | // Returns the number of states added to all_keys. 360 | size_t dedup(Keys* keys_by_depth, Values* values_by_depth, 361 | Keys* all_keys, 362 | const Keys& new_keys, const Values &new_values) { 363 | // Set to true iff a state should be discarded due to the 364 | // presence of an earlier duplicate. 365 | std::vector discard(new_values.size()); 366 | Keys new_all_keys; 367 | 368 | using PairStream = StreamPairer; 369 | 370 | // Iterate through the new keys / values in tandem. If new_* 371 | // has multiple runs, interleave the runs togehter into a 372 | // single sorted stream. 373 | SortedStreamInterleaver 374 | new_stream; 375 | for (int run = 0; run < new_keys.run_count(); ++run) { 376 | auto keyinfo = new_keys.run(run); 377 | auto valinfo = new_values.run(run); 378 | if (keyinfo.first != keyinfo.second) { 379 | auto keystream = 380 | new KeyStream(keyinfo.first, keyinfo.second); 381 | auto valstream = 382 | new ValueStream(valinfo.first, valinfo.second); 383 | new_stream.add_stream(new PairStream(keystream, 384 | valstream)); 385 | } 386 | } 387 | 388 | { 389 | Keys::WriteRun key_writer { keys_by_depth }; 390 | Values::WriteRun value_writer { values_by_depth }; 391 | Keys::WriteRun all_keys_writer { &new_all_keys }; 392 | 393 | KeyStream merged_stream { all_keys->begin(), 394 | all_keys->end() }; 395 | 396 | KeyCompressor compress { keys_by_depth }; 397 | KeyCompressor compress_merged { &new_all_keys }; 398 | 399 | merged_stream.next(); 400 | new_stream.next(); 401 | 402 | while (1) { 403 | bool have_new = !new_stream.empty(); 404 | bool have_old = !merged_stream.empty(); 405 | if (have_new && have_old) { 406 | auto new_st = new_stream.value().first; 407 | auto old_st = merged_stream.value(); 408 | if (old_st < new_st) { 409 | compress_merged.pack(old_st.bytes()); 410 | merged_stream.next(); 411 | } else if (old_st == new_st) { 412 | compress_merged.pack(old_st.bytes()); 413 | merged_stream.next(); 414 | new_stream.next(); 415 | } else { 416 | compress.pack(new_st.bytes()); 417 | compress_merged.pack(new_st.bytes()); 418 | values_by_depth->push_back(new_stream.value().second); 419 | new_stream.next(); 420 | } 421 | } else if (have_old) { 422 | auto old_st = merged_stream.value(); 423 | compress_merged.pack(old_st.bytes()); 424 | merged_stream.next(); 425 | } else if (have_new) { 426 | auto new_st = new_stream.value().first; 427 | compress.pack(new_st.bytes()); 428 | compress_merged.pack(new_st.bytes()); 429 | values_by_depth->push_back(new_stream.value().second); 430 | new_stream.next(); 431 | } else { 432 | break; 433 | } 434 | } 435 | } 436 | 437 | std::swap(*all_keys, new_all_keys); 438 | 439 | auto last_run = values_by_depth->run(values_by_depth->run_count() - 1); 440 | size_t count = std::distance(last_run.first, last_run.second); 441 | return count; 442 | } 443 | }; 444 | 445 | #endif 446 | -------------------------------------------------------------------------------- /src/snakebird/level00.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "........................" 6 | ". .. * ." 7 | ". .... ." 8 | ". .. ." 9 | ". . O . ." 10 | ". >>R . . .. O ." 11 | "......... .. ..." 12 | "........................"; 13 | 14 | 15 | using St = State>; 16 | St::Map map(base_map); 17 | St st(map); 18 | st.print(map); 19 | 20 | EXPECT_EQ(29, search(st, map)); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /src/snakebird/level01.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".........." 6 | ". * ." 7 | ". ." 8 | ". . ." 9 | ". O .O. ." 10 | ". ." 11 | ". .>G ." 12 | ". .... ." 13 | ". .... ." 14 | ". ... ." 15 | "~~~~~~~~~~"; 16 | 17 | using St = State>; 18 | St::Map map(base_map); 19 | St st(map); 20 | st.print(map); 21 | 22 | EXPECT_EQ(16, search(st, map)); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/snakebird/level02.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..........." 6 | ".... ." 7 | ".... * ." 8 | ".... ." 9 | ". O. v ." 10 | ". G< O." 11 | ". ..... ." 12 | "........ ." 13 | "........ ." 14 | "........ ." 15 | "........ ." 16 | "~~~~~~~~~~~"; 17 | 18 | using St = State>; 19 | St::Map map(base_map); 20 | St st(map); 21 | st.print(map); 22 | 23 | EXPECT_EQ(25, search(st, map)); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/snakebird/level03.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".........." 6 | ". * ." 7 | ".>B ." 8 | ".^O O ." 9 | ". ~ ." 10 | ". ~......" 11 | "..... ...." 12 | ".... ...." 13 | ". . ..." 14 | "~~~~~~~~~~"; 15 | 16 | using St = State>; 17 | St::Map map(base_map); 18 | St st(map); 19 | 20 | st.print(map); 21 | 22 | EXPECT_EQ(27, search(st, map)); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/snakebird/level04.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".........." 6 | ". ." 7 | ". >>G * ." 8 | ". ^. ." 9 | ". ." 10 | "..~ ~. ." 11 | ". ~ ." 12 | ". ~~.~ ." 13 | ". O ." 14 | ". . ." 15 | ". .. ." 16 | ". .. ." 17 | "~~~~~~~~~~"; 18 | 19 | using St = State>; 20 | St::Map map(base_map); 21 | St st(map); 22 | 23 | st.print(map); 24 | 25 | EXPECT_EQ(30, search(st, map)); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/snakebird/level05.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "........" 6 | ". * ." 7 | ". . ." 8 | ".O O. ." 9 | ". ." 10 | ".~. . ." 11 | ". >R~~ ." 12 | ". ^.~ ." 13 | "~~~~~~~~"; 14 | 15 | using St = State>; 16 | St::Map map(base_map); 17 | St st(map); 18 | 19 | st.print(map); 20 | 21 | EXPECT_EQ(24, search(st, map)); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /src/snakebird/level06.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "............" 6 | ". ." 7 | ". .. ." 8 | ". .. ." 9 | ". .. ." 10 | ". ... ." 11 | ". .O . ." 12 | ". . *." 13 | ". >B . ." 14 | ". ^..~ ." 15 | ". ^ ... ." 16 | ". ." 17 | "~~~~~~~~~~~~"; 18 | 19 | 20 | using St = State>; 21 | St::Map map(base_map); 22 | St st(map); 23 | 24 | st.print(map); 25 | 26 | EXPECT_EQ(36, search(st, map)); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /src/snakebird/level07.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".............." 6 | ". ." 7 | ". * ." 8 | ". ." 9 | ". . ." 10 | ". ." 11 | ". . ." 12 | ". . ." 13 | ". . . B<< ." 14 | ". . G<<^ ." 15 | ". .... ." 16 | "~~~~~~~~~~~~~~"; 17 | 18 | using St = State>; 19 | St::Map map(base_map); 20 | St st(map); 21 | st.print(map); 22 | 23 | EXPECT_EQ(43, search(st, map)); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/snakebird/level08.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".................." 6 | ". * ." 7 | ". ." 8 | ". >>R ...." 9 | ". >^G<< ~~~..." 10 | ".......~~~ ~ ." 11 | ". ..... ~~.~ ." 12 | "~~~~~~~~~~~~~~~~~~"; 13 | 14 | using St = State>; 15 | St::Map map(base_map); 16 | St st(map); 17 | st.print(map); 18 | 19 | EXPECT_EQ(29, search(st, map)); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /src/snakebird/level09.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".........." 6 | ". * ." 7 | ". ." 8 | ". ." 9 | ". ." 10 | ". ." 11 | ". ." 12 | ". .~ ." 13 | ". ." 14 | ". ." 15 | ". ~~ ." 16 | ". . ." 17 | ". . ." 18 | ". ." 19 | ". ." 20 | ".>>R B< ." 21 | ".^.....^<." 22 | "..... ..^." 23 | "~~~~~~~~~~"; 24 | 25 | using St = State>; 26 | St::Map map(base_map); 27 | St st(map); 28 | st.print(map); 29 | 30 | EXPECT_EQ(37, search(st, map)); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /src/snakebird/level10.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".............." 6 | ". ... ." 7 | ". .... * ." 8 | ". . ." 9 | ". O . v.. ." 10 | ". R<<. ." 11 | ". .... .. ." 12 | ". ... . ." 13 | ". . O ." 14 | ". . .. ." 15 | ". . .. ." 16 | ". .. .. ." 17 | ". .... ." 18 | "~~~~~~~~~~~~~~"; 19 | 20 | using St = State>; 21 | St::Map map(base_map); 22 | St st(map); 23 | st.print(map); 24 | 25 | EXPECT_EQ(33, search(st, map)); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/snakebird/level11.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".............." 6 | ". ~~ ." 7 | ". ~~~.~~ ." 8 | ". ~......~ ." 9 | ". .. .... ." 10 | ". .. O O . ." 11 | ". .O.O O. ." 12 | ". * >G O .. ." 13 | ". ^... O.. ." 14 | ". ^<<.... ." 15 | ". ^ .. ." 16 | ". ^ ." 17 | "~~~~~~~~~~~~~~"; 18 | 19 | using St = State>; 20 | St::Map map(base_map); 21 | St st(map); 22 | st.print(map); 23 | 24 | EXPECT_EQ(35, search(st, map)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/snakebird/level12.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..............." 6 | ". . ." 7 | ". . ." 8 | ". ~ ." 9 | ". v B ." 10 | ". >>^ ." 11 | ". . ." 12 | ". ~ ." 13 | ". * ." 14 | ". ~.~ ~~~. ." 15 | ". .. ." 16 | ". ." 17 | ". O ~ ." 18 | ". .~O ." 19 | ". ... ." 20 | ". ... ." 21 | "~~~~~~~~~~~~~~~"; 22 | 23 | using St = State>; 24 | St::Map map(base_map); 25 | St st(map); 26 | st.print(map); 27 | 28 | EXPECT_EQ(52, search(st, map)); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /src/snakebird/level13.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "................." 6 | ". ." 7 | ". ." 8 | ". ~~~~ ." 9 | ". ...~ ." 10 | ". ...~ . ." 11 | ". . ." 12 | ". * B<< ." 13 | ". .^ ." 14 | ". . ." 15 | ". ... G<< ." 16 | ". ... .^ ." 17 | ". .^ ." 18 | ". . ." 19 | ". . ." 20 | "~~~~~~~~~~~~~~~~~"; 21 | 22 | using St = State>; 23 | St::Map map(base_map); 24 | St st(map); 25 | st.print(map); 26 | 27 | EXPECT_EQ(44, search(st, map)); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/snakebird/level14.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "............." 6 | ". ." 7 | ". ." 8 | ". ~ ." 9 | ". * >B ." 10 | ". >R . ." 11 | ". ^. . ." 12 | ". . . . ." 13 | ". . . . ." 14 | ". . . . ." 15 | "~~~~~~~~~~~~~"; 16 | 17 | using St = State>; 18 | St::Map map(base_map); 19 | St st(map); 20 | st.print(map); 21 | 22 | EXPECT_EQ(24, search(st, map)); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/snakebird/level15.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..............." 6 | ". ." 7 | ". ." 8 | ". . ." 9 | ". . ." 10 | ". . * ." 11 | ". ." 12 | ". . . ." 13 | ". >>R ." 14 | ". .^ v ." 15 | ". .G<< ." 16 | ". .... ." 17 | "~~~~~~~~~~~~~~~"; 18 | 19 | using St = State>; 20 | St::Map map(base_map); 21 | St st(map); 22 | st.print(map); 23 | 24 | EXPECT_EQ(34, search(st, map)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/snakebird/level16.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".........." 6 | ". ." 7 | ". * ." 8 | ". ." 9 | ". ." 10 | ". ." 11 | ". ." 12 | ". R<< . ." 13 | ". .^ ." 14 | ". . ." 15 | ". .>>G ." 16 | ". B<<. ." 17 | ". . ." 18 | ". . ." 19 | ". . ." 20 | "~~~~~~~~~~"; 21 | 22 | using St = State>; 23 | St::Map map(base_map); 24 | St st(map); 25 | st.print(map); 26 | 27 | EXPECT_EQ(65, search(st, map)); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/snakebird/level17.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "............." 6 | ". ." 7 | ". ." 8 | ". .~ ." 9 | ". .~ ." 10 | ". ~G<< ." 11 | ". >R^ ." 12 | ". ..~ ~..^ ." 13 | ". ..~~ ~.. ." 14 | ". .~ ~. ." 15 | ". * ." 16 | ". ~ ." 17 | ". .~ ." 18 | ". .~ ." 19 | ". ~~~ ." 20 | "~~~~~~~~~~~~~"; 21 | 22 | using St = State>; 23 | St::Map map(base_map); 24 | St st(map); 25 | st.print(map); 26 | 27 | EXPECT_EQ(68, search(st, map)); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/snakebird/level18.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".................." 6 | ". ." 7 | ". ." 8 | ". .. ." 9 | ". .. ." 10 | ". .. ." 11 | ". . ." 12 | ". * . ." 13 | ". . ~ B<< ." 14 | ". . .G<<^ ." 15 | ". . . .... ." 16 | ". .. ." 17 | ". ... ." 18 | ". .. ." 19 | ". ..~ ." 20 | "~~~~~~~~~~~~~~~~~~"; 21 | 22 | using St = State>; 23 | St::Map map(base_map); 24 | St st(map); 25 | st.print(map); 26 | 27 | EXPECT_EQ(35, search(st, map)); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/snakebird/level19.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "................" 6 | ". ." 7 | ". ~ ." 8 | ".~ . ." 9 | ".. * O." 10 | ". B< ." 11 | ". >>G ." 12 | ". ^R< ." 13 | ". ^.^ ." 14 | ". ^.^ ." 15 | ". .^ ." 16 | ". . ." 17 | "~~~~~~~~~~~~~~~~"; 18 | 19 | using St = State>; 20 | St::Map map(base_map); 21 | St st(map); 22 | 23 | st.print(map); 24 | 25 | EXPECT_EQ(47, search(st, map)); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/snakebird/level20.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "................." 6 | ". ." 7 | ". .. ." 8 | ". .... ." 9 | ". >B .O . ." 10 | ". * >G O . ." 11 | ". .. . .... ." 12 | ". ..~ .. ." 13 | ". .... . .. ." 14 | ". ... . .. ." 15 | "~~~~~~~~~~~~~~~~~"; 16 | 17 | using St = State>; 18 | St::Map map(base_map); 19 | St st(map); 20 | st.print(map); 21 | 22 | EXPECT_EQ(50, search(st, map)); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/snakebird/level21.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "............." 6 | ". ." 7 | ". * ." 8 | ". # ." 9 | ". O . O ." 10 | ". ." 11 | ". ." 12 | ". >B ." 13 | ". ###^. ## ." 14 | ". ...^O ##. ." 15 | ". .. ^< .. ." 16 | ". .. .. ." 17 | ". .. .. ." 18 | "~~~~~~~~~~~~~"; 19 | 20 | using St = State>; 21 | St::Map map(base_map); 22 | St st(map); 23 | st.print(map); 24 | 25 | EXPECT_EQ(39, search(st, map)); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/snakebird/level22.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "............." 6 | ". * ." 7 | ". ." 8 | ". ." 9 | ". ." 10 | ". ." 11 | ". >>R ." 12 | ". .. 00 ." 13 | ". .. . 00 ." 14 | ". .. .. ." 15 | ". ....... ." 16 | ". ....... ." 17 | "~~~~~~~~~~~~~"; 18 | 19 | using St = State>; 20 | St::Map map(base_map); 21 | St st(map); 22 | st.print(map); 23 | 24 | EXPECT_EQ(45, search(st, map)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/snakebird/level23.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..................." 6 | ". ." 7 | ". ." 8 | ". # ." 9 | ". ###### ." 10 | ". 0 .....# ." 11 | ". 111 ...# ." 12 | ". >>>>B ...... ." 13 | ". ^...... ....... ." 14 | ". ^......0....... ." 15 | ". ^ .*#. . ." 16 | ". ^ . #. . . ." 17 | ". ^ . # . ." 18 | ". ^ . # . ." 19 | ". . # # . ." 20 | ". . . . ." 21 | "~~~~~~~~~~~~~~~~~~~"; 22 | 23 | using St = State>; 24 | St::Map map(base_map); 25 | St st(map); 26 | st.print(map); 27 | 28 | EXPECT_EQ(53, search(st, map)); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /src/snakebird/level24.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".........." 6 | ". O ." 7 | ". . ." 8 | ". ." 9 | ". ." 10 | ". ." 11 | ". ." 12 | ". 000 v ." 13 | ". 000B<* ." 14 | ". 0 R< ." 15 | ". ...^ ." 16 | ". ... ." 17 | ". . ." 18 | "~~~~~~~~~~"; 19 | 20 | using St = State>; 21 | St::Map map(base_map); 22 | St st(map); 23 | st.print(map); 24 | 25 | EXPECT_EQ(26, search(st, map)); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/snakebird/level25.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..............." 6 | ". * ." 7 | ". ." 8 | ". . ." 9 | ". ." 10 | ". ." 11 | ". ." 12 | ". ." 13 | ". 0 ." 14 | ". ... ." 15 | ". 0 0 ." 16 | ". ." 17 | ". >G 0 B< ." 18 | ". ^. .^ ." 19 | ". ^. . ." 20 | ". . . ." 21 | ". . . ." 22 | "~~~~~~~~~~~~~~~"; 23 | 24 | using St = State>; 25 | St::Map map(base_map); 26 | St st(map); 27 | st.print(map); 28 | 29 | EXPECT_EQ(35, search(st, map)); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /src/snakebird/level26.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "................" 6 | ". ... ." 7 | ". ....." 8 | ". #...." 9 | ". # ." 10 | ". O # * ." 11 | ". B< ." 12 | ". R #...." 13 | ". ^ #...." 14 | ". ^0 #...." 15 | ". .########.. ." 16 | ". ... ........" 17 | "~~~~~~~~~~~~~~~~"; 18 | 19 | using St = State>; 20 | St::Map map(base_map); 21 | St st(map); 22 | st.print(map); 23 | 24 | EXPECT_EQ(35, search(st, map)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/snakebird/level27.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "............" 6 | ". O ." 7 | ". .. ." 8 | ". .. ." 9 | ". .. ." 10 | ". ." 11 | ". ." 12 | ". # # ." 13 | ". .. .. ." 14 | ". # 0 # ." 15 | ". ." 16 | ". >G 0 R<* ." 17 | ". ......^ ." 18 | ". ......^ ." 19 | ". ...... ." 20 | "~~~~~~~~~~~~"; 21 | 22 | using St = State>; 23 | St::Map map(base_map); 24 | St st(map); 25 | st.print(map); 26 | 27 | EXPECT_EQ(49, search(st, map)); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/snakebird/level28.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".........." 6 | ". * ." 7 | ". ." 8 | ". ." 9 | ". . 0 ." 10 | ". ... # ." 11 | ". O. ." 12 | ". . ." 13 | ". #.0 ." 14 | ". >>B ." 15 | ". .G<< ." 16 | ". ..... ." 17 | ". ..... ." 18 | "~~~~~~~~~~"; 19 | 20 | using St = State>; 21 | St::Map map(base_map); 22 | St st(map); 23 | st.print(map); 24 | 25 | EXPECT_EQ(49, search(st, map)); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/snakebird/level29.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".............." 6 | ". * ." 7 | ". ." 8 | ". ." 9 | ". ." 10 | ". ." 11 | ". ." 12 | ". R<< ." 13 | ". 00 ." 14 | ". 00 ." 15 | ". 112233 ." 16 | ". 112233 B<< ." 17 | ". ..... .. ." 18 | ". .......... ." 19 | ". ..... .... ." 20 | "~~~~~~~~~~~~~~"; 21 | 22 | using St = State>; 23 | St::Map map(base_map); 24 | St st(map); 25 | st.print(map); 26 | 27 | EXPECT_EQ(45, search(st, map)); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/snakebird/level30.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..............." 6 | ". ." 7 | ". ." 8 | ". ." 9 | ". * ." 10 | ". ." 11 | ". . .T. ." 12 | ". T ." 13 | ". B<<< . ." 14 | ". .. . . ." 15 | ". . . . ." 16 | ". . . . ." 17 | "~~~~~~~~~~~~~~~"; 18 | 19 | using St = State>; 20 | St::Map map(base_map); 21 | St st(map); 22 | st.print(map); 23 | 24 | EXPECT_EQ(15, search(st, map)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/snakebird/level31.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "................." 6 | ". ." 7 | ". .... ." 8 | ". .. ." 9 | ". ." 10 | ". T ." 11 | ". . ." 12 | ". G . ." 13 | ". * T ^ ." 14 | ". . ^< ." 15 | ". ...... ." 16 | ". .. ." 17 | "~~~~~~~~~~~~~~~~~"; 18 | 19 | using St = State>; 20 | St::Map map(base_map); 21 | St st(map); 22 | st.print(map); 23 | 24 | EXPECT_EQ(8, search(st, map)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/snakebird/level32.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".............." 6 | ". ." 7 | ". . ." 8 | ". . . ." 9 | ". * . 0T ." 10 | ". .B< ... ." 11 | ". T>R . . ." 12 | ". ......## ." 13 | ". . . ." 14 | "~~~~~~~~~~~~~~"; 15 | 16 | using St = State>; 17 | St::Map map(base_map); 18 | St st(map); 19 | st.print(map); 20 | 21 | EXPECT_EQ(21, search(st, map)); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /src/snakebird/level33.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..............." 6 | ". ." 7 | ". * ." 8 | ". ." 9 | ". ###..... ." 10 | ". O T. . ." 11 | ". .## O . ." 12 | ". . . ." 13 | ". . . ." 14 | ". # T . ." 15 | ". G< . ." 16 | ". ##.#..^ . ." 17 | ". ... .. . ." 18 | "~~~~~~~~~~~~~~~"; 19 | 20 | using St = State>; 21 | St::Map map(base_map); 22 | St st(map); 23 | st.print(map); 24 | 25 | EXPECT_EQ(42, search(st, map)); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/snakebird/level34.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..........." 6 | ". ." 7 | ". ..T.. ." 8 | ". * .. .. ." 9 | ". 0 ." 10 | ". . >B ." 11 | ". T G< ." 12 | ". ..... ." 13 | "~~~~~~~~~~~"; 14 | 15 | using St = State>; 16 | St::Map map(base_map); 17 | St st(map); 18 | st.print(map); 19 | 20 | EXPECT_EQ(17, search(st, map)); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /src/snakebird/level35.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..........." 6 | ". ....... ." 7 | ". ..... ." 8 | ". ..... ." 9 | ". ." 10 | ". . ." 11 | ". # ." 12 | ". O ." 13 | ". T . ." 14 | ". . ." 15 | ". ." 16 | ". * >>B ." 17 | ". ^. ." 18 | ". . ." 19 | ". T ." 20 | ". ## ." 21 | ". O ." 22 | ". .. ." 23 | ". .. ." 24 | ". .. ." 25 | "~~~~~~~~~~~"; 26 | 27 | using St = State>; 28 | St::Map map(base_map); 29 | St st(map); 30 | st.print(map); 31 | 32 | EXPECT_EQ(29, search(st, map)); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /src/snakebird/level36.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "...................." 6 | ". ." 7 | ". .... ... ." 8 | ". .... ... ." 9 | ". .. ." 10 | ". . * ." 11 | ". . ." 12 | ". . >>R # ... ." 13 | ". T>^G<< ### ." 14 | ". ......##T # ." 15 | ". .. ###.# ." 16 | ". . ." 17 | "~~~~~~~~~~~~~~~~~~~~"; 18 | 19 | using St = State>; 20 | St::Map map(base_map); 21 | St st(map); 22 | st.print(map); 23 | 24 | EXPECT_EQ(29, search(st, map)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/snakebird/level37.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".............." 6 | ". . ." 7 | ". . ." 8 | ". ..... ." 9 | ". . . ." 10 | ". T . ." 11 | ". . . ." 12 | ". .G<>R. ." 14 | ". ..... ." 15 | "~~~~~~~~~~~~~~"; 16 | 17 | using St = State>; 18 | St::Map map(base_map); 19 | St st(map); 20 | st.print(map); 21 | 22 | EXPECT_EQ(16, search(st, map)); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/snakebird/level38.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..............." 6 | ". ." 7 | ". ." 8 | ". * ." 9 | ". T ." 10 | ". G<<< ." 11 | ". ..... ." 12 | ". T . ." 13 | ". . . ." 14 | ". >>>R ... ." 15 | ". ....... ." 16 | "~~~~~~~~~~~~~~~"; 17 | 18 | using St = State>; 19 | St::Map map(base_map); 20 | St st(map); 21 | st.print(map); 22 | 23 | EXPECT_EQ(28, search(st, map)); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/snakebird/level39.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "................" 6 | ". ..... ." 7 | ". .... ." 8 | ". .... ." 9 | ". ." 10 | ". * ." 11 | ". ." 12 | ". ." 13 | ". .... ." 14 | ". .... ." 15 | ". ... ." 16 | ". .... ." 17 | ". >>G ..... ." 18 | ". ^00 .... ." 19 | ". ^11 ..... ." 20 | ". .... ...... ." 21 | ". ...... ..... ." 22 | ". .... . . ." 23 | ". ... . . ." 24 | "~~~~~~~~~~~~~~~~"; 25 | 26 | using St = State>; 27 | St::Map map(base_map); 28 | St st(map); 29 | st.print(map); 30 | 31 | EXPECT_EQ(53, search(st, map)); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /src/snakebird/level40.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "......................" 6 | ". ." 7 | ". * ." 8 | ". ." 9 | ". ." 10 | ". . ." 11 | ". 00 ." 12 | ". 11 ." 13 | ". . ." 14 | ". >v ." 15 | ". .>>B ." 16 | ". G<<<< ." 17 | ". . ." 18 | ". . ." 19 | ". . ." 20 | "~~~~~~~~~~~~~~~~~~~~~~"; 21 | 22 | using St = State>; 23 | St::Map map(base_map); 24 | St st(map); 25 | st.print(map); 26 | 27 | EXPECT_EQ(51, search(st, map)); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/snakebird/level41.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "...................." 6 | ". ." 7 | ". * . ." 8 | ". . ." 9 | ". ." 10 | ". >B G< . ." 11 | ". ^O O^ .. ." 12 | ". . .. ." 13 | ". . .. . ." 14 | ". . .. . ." 15 | "~~~~~~~~~~~~~~~~~~~~"; 16 | 17 | using St = State>; 18 | St::Map map(base_map); 19 | St st(map); 20 | st.print(map); 21 | 22 | EXPECT_EQ(34, search(st, map)); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/snakebird/level42.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..............." 6 | ". ##. ." 7 | ". ... ." 8 | ". . ." 9 | ". . >R . * ." 10 | ". . G< ." 11 | ". O#O. . ." 12 | ". # . ." 13 | ". .. . . ." 14 | ". .. . . ." 15 | ". .. . . ." 16 | "~~~~~~~~~~~~~~~"; 17 | 18 | using St = State>; 19 | St::Map map(base_map); 20 | St st(map); 21 | st.print(map); 22 | 23 | EXPECT_EQ(42, search(st, map)); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/snakebird/level43.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "....................." 6 | ". ." 7 | ". .. ." 8 | ". ... ." 9 | ". ... * ." 10 | ". . ." 11 | ". B<< ." 12 | ". # >>R # ." 13 | ". 0O0 ." 14 | ". 000 . ." 15 | ". .#. . ." 16 | ". ... . ." 17 | "~~~~~~~~~~~~~~~~~~~~~"; 18 | 19 | using St = State>; 20 | St::Map map(base_map); 21 | St st(map); 22 | st.print(map); 23 | 24 | EXPECT_EQ(36, search(st, map)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/snakebird/level44.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".............." 6 | ". ." 7 | ". T ." 8 | ". O * ." 9 | ". #O ." 10 | ". # ." 11 | ". >R T ." 12 | ". G< ." 13 | ". #.... #. ." 14 | ". .. . . ." 15 | ". . . . ." 16 | "~~~~~~~~~~~~~~"; 17 | 18 | using St = State>; 19 | St::Map map(base_map); 20 | St st(map); 21 | st.print(map); 22 | 23 | EXPECT_EQ(36, search(st, map)); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/snakebird/level45.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".............." 6 | ". ." 7 | ". 0 ." 8 | ". #1 ." 9 | ". 1 ." 10 | ". . . * ." 11 | ". # ." 12 | ". # ." 13 | ". >>R #B<< ." 14 | ". ^.##O .. ." 15 | ". .. .. ." 16 | ". . . ." 17 | ". O ." 18 | ". ." 19 | ". ." 20 | ". . ." 21 | ". #.# ." 22 | "~~~~~~~~~~~~~~"; 23 | 24 | using St = State>; 25 | St::Map map(base_map); 26 | St st(map); 27 | st.print(map); 28 | 29 | EXPECT_EQ(77, search(st, map)); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /src/snakebird/levelstar1.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "........................." 6 | ". ." 7 | ". ." 8 | ". ." 9 | ". .##### ." 10 | ". . ." 11 | ". . ." 12 | ". . #### ." 13 | ". * # ." 14 | ". ." 15 | ". B<< . ." 16 | ".... 0^ ." 17 | ".... ." 18 | ".... G<< . ." 19 | "... R<<< ." 20 | ". 0 ." 21 | ". ..... ." 22 | ". ..... ." 23 | ". ... ." 24 | "~~~~~~~~~~~~~~~~~~~~~~~~~"; 25 | 26 | using St = State>; 27 | St::Map map(base_map); 28 | St st(map); 29 | st.print(map); 30 | 31 | EXPECT_EQ(75, search(st, map)); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /src/snakebird/levelstar2.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "..................." 6 | ". ... ." 7 | ". ....... ." 8 | ". . O O .. ." 9 | ". ..O.O.O.. ... ." 10 | ". .OOOOOOO...... ." 11 | ". .. .O.O. R<< *. ." 12 | ". ..OOOOOOO..... ." 13 | ". ...O.O.O.... ." 14 | ". . O O . ." 15 | ". ...... ." 16 | ". ...... ." 17 | ". ... ." 18 | "~~~~~~~~~~~~~~~~~~~"; 19 | 20 | using St = State>; 21 | St::Map map(base_map); 22 | St st(map); 23 | st.print(map); 24 | 25 | EXPECT_EQ(60, search(st, map)); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/snakebird/levelstar3.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "...................." 6 | ". ." 7 | ". . ." 8 | ". . ." 9 | ". . ." 10 | ". . ." 11 | ". * 000 # ." 12 | ". 0R0 ." 13 | ". 0^0 ." 14 | ". ^ ." 15 | ". >B^ # ." 16 | ". . ^>>G.## ." 17 | ". .. #.## ." 18 | ". ." 19 | ". .. ." 20 | ". . ." 21 | "~~~~~~~~~~~~~~~~~~~~"; 22 | 23 | using St = State>; 24 | St::Map map(base_map); 25 | St st(map); 26 | st.print(map); 27 | 28 | EXPECT_EQ(63, search(st, map)); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /src/snakebird/levelstar4.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "............." 6 | ". * ." 7 | ". ." 8 | ". ." 9 | ". ." 10 | ". # ." 11 | ". 0 0 ." 12 | ". 1 #1 ." 13 | ". 2 2 ." 14 | ". G<<#>>R ." 15 | ". ......... ." 16 | ". ..... ." 17 | ". .. ." 18 | "~~~~~~~~~~~~~"; 19 | 20 | using St = State>; 21 | St::Map map(base_map); 22 | St st(map); 23 | st.print(map); 24 | 25 | EXPECT_EQ(44, search(st, map)); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/snakebird/levelstar5.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".............." 6 | ". .......... ." 7 | ". 0..... . ." 8 | ". 0 * ." 9 | ". 0 # . ." 10 | ". . .. ." 11 | ". .T . ." 12 | ". . . ." 13 | ". ..#. ." 14 | ". T ." 15 | ". ." 16 | ". >>Rv ." 17 | ". ^B<< ." 18 | ". .... ." 19 | ". .. ." 20 | "~~~~~~~~~~~~~~"; 21 | 22 | using St = State>; 23 | St::Map map(base_map); 24 | St st(map); 25 | st.print(map); 26 | 27 | EXPECT_EQ(69, search(st, map)); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/snakebird/levelstar6.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | ".............." 6 | ". ." 7 | ". * ." 8 | ". ." 9 | ". . ." 10 | ". 00 ." 11 | ". . . ." 12 | ". .. . ." 13 | ". .. ." 14 | ". 11 ." 15 | ". .. ." 16 | ". .. 222.. ." 17 | ". .. .. ." 18 | ". R<<222 ." 19 | ". >>G..B<< ." 20 | ". ........ ." 21 | ". ....... ." 22 | "~~~~~~~~~~~~~~"; 23 | 24 | using St = State>; 25 | St::Map map(base_map); 26 | St st(map); 27 | st.print(map); 28 | 29 | EXPECT_EQ(90, search(st, map)); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /src/snakebird/levelvoid.cc: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | int main() { 4 | const char* base_map = 5 | "................." 6 | ". .... ." 7 | ". .... ." 8 | ". .O. O ." 9 | ". .O. ." 10 | ". 0 ." 11 | ". B< ." 12 | ". >GR< ." 13 | ". ....# ." 14 | ". ... ." 15 | ". ... * ." 16 | ". . ." 17 | ". . # ." 18 | ". . ." 19 | ". . ." 20 | ". . ." 21 | ". . ." 22 | ". . ." 23 | "~~~~~~~~~~~~~~~~~"; 24 | 25 | using St = State>; 26 | St::Map map(base_map); 27 | St st(map); 28 | st.print(map); 29 | 30 | EXPECT_EQ(52, search(st, map)); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /src/snakebird/main.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++ -*- 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "bit-packer.h" 14 | #include "compress.h" 15 | #include "file-backed-array.h" 16 | #include "snakebird/snakebird.h" 17 | #include "search.h" 18 | 19 | #define EXPECT_EQ(wanted, actual) \ 20 | do { \ 21 | printf("Running %s\n", #actual); \ 22 | auto tmp = actual; \ 23 | if (tmp != wanted) { \ 24 | fprintf(stderr, "Error: expected %s => %d, got %d\n", #actual, wanted, tmp); \ 25 | } \ 26 | } while (0) 27 | 28 | template 29 | int search(St start_state, const Map& map) { 30 | class SnakeBirdSearch { 31 | public: 32 | static void start_iteration(int depth) { 33 | printf("depth: %d\n", depth); 34 | } 35 | 36 | static void trace(const Map& setup, const St& state, int depth) { 37 | printf("Move %d\n", depth); 38 | state.print(setup); 39 | } 40 | }; 41 | 42 | BreadthFirstSearch bfs; 43 | return bfs.search(start_state, map); 44 | } 45 | -------------------------------------------------------------------------------- /src/snakebird/snakebird.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++ -*- 2 | // 3 | // The game logic for the game Snakebird. 4 | // 5 | // The game is played on a 2d map, with Snakebirds, fruit, and an 6 | // exit, with the goal being to have the Snakebirds eat all the fruit 7 | // and then leave by the exit. 8 | // 9 | // .......... 10 | // . * . 11 | // .>B . 12 | // .^O O . 13 | // . ~ . 14 | // . ~...... 15 | // ..... .... 16 | // .... .... 17 | // . . ... 18 | // ~~~~~~~~~~ 19 | // 20 | // The Snakebirds move in the cardinal directions. If a Snakebird moves 21 | // onto a fruit, the Snakebird will grow by one step. Otherwise all 22 | // segments of the snake will move forward by one step. If the Snakebird 23 | // moves on top of another Snakebird or some other movable object, the 24 | // object will be pushed in that direction (and will in turn push 25 | // other objects). A Snakebird can't push itself, directly or indirectly. 26 | // 27 | // A Snakebird or pushable object that is not supported from below by 28 | // the ground, another object, or a fruit, will drop down until it is 29 | // supported. 30 | // 31 | // Though the map is logically 2d, both the internal physical 32 | // representation and the external coordinate system are set up as 33 | // a row-major 1d array. For a map of size R*C, the top left is 34 | // represented by coordinate 0, the leftmost space of the second 35 | // row as C+1, and the bottom right as R*C-1. 36 | // 37 | // There is no bounds checking when e.g. checking the state of 38 | // neighboring spaces. Safety is ensured by having all maps be 39 | // surrounded by walls. These walls must be present in the original 40 | // ASCII drawing of a map; they will not be added in automatically. 41 | // 42 | // All this code is specialized to the properies of a specific puzzle 43 | // instance (e.g. shape of map, number of objects). 44 | 45 | #ifndef GAME_H 46 | #define GAME_H 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #include "bit-packer.h" 56 | #include "util.h" 57 | 58 | enum Direction { 59 | UP, RIGHT, DOWN, LEFT, 60 | }; 61 | 62 | // A row-major coordinate into the map. 63 | using Coord = int; 64 | 65 | // A puzzle scenario description. All other classes are parametrized 66 | // with this. 67 | // 68 | // H: Map height 69 | // W: Map width 70 | // FruitCount: Number of initial fruit on the board. 71 | // SnakeCount: Number of initial snakes on the board. 72 | // SnakeMaxLen: The maximum size that a snake could theoretically 73 | // grow to. (Normally the maximum initial length + number of fruit). 74 | // GadgetCount: Number of other pushable objects on the board. 75 | // TeleporterCount: Number of teleporters on the map. 76 | template 79 | struct Setup { 80 | static const Coord H = H_; 81 | static const Coord W = W_; 82 | static const int FruitCount = FruitCount_; 83 | static const int SnakeCount = SnakeCount_; 84 | static const int SnakeMaxLen = SnakeMaxLen_; 85 | static const int GadgetCount = GadgetCount_; 86 | static const int TeleporterCount = TeleporterCount_; 87 | static const int ObjCount = SnakeCount + GadgetCount; 88 | 89 | static const int MapSize = Setup::H * Setup::W; 90 | 91 | // Number of bits used to pack a direction. 92 | static const int kDirBits = 2; 93 | static const uint64_t kDirMask = mask_n_bits(kDirBits); 94 | static const int kIndexBits = integer_length::value; 95 | static const int kLenBits = integer_length::value; 96 | 97 | // Converting between directions and linear coordinate deltas. 98 | static Coord apply_direction(Direction dir) { 99 | static Coord deltas[] = { -W, 1, W, -1 }; 100 | return deltas[dir]; 101 | } 102 | }; 103 | 104 | // The dynamic representation of a snake. 105 | // 106 | // A snake consists of a queue of segments, with each segment 107 | // being orthogonally adjacent to the others. As the snake moves, 108 | // the head of the bird (i.e. segment 0) moves by one space to the 109 | // target space, and all the other segments move to the location 110 | // vacated by the previous segment. If a snake grows, none of 111 | // the existing segments move. Instead a new segment is added to 112 | // the head of the queue. 113 | template 114 | class Snake { 115 | public: 116 | static const int kTailBits = ((Setup::SnakeMaxLen - 1) * Setup::kDirBits); 117 | 118 | Snake() : tail_(0), len_(0) { 119 | i_[0] = 0; 120 | } 121 | 122 | Snake(Coord i) 123 | : tail_(0), 124 | len_(1) { 125 | assert(i < Setup::MapSize); 126 | i_[0] = i; 127 | } 128 | 129 | // Extends the head of the snake in the given direction, without 130 | // shortening the snake at the tail. 131 | void grow(Direction dir) { 132 | std::copy(&i_[0], &i_[len_], &i_[1]); 133 | i_[0] = i_[1] + Setup::apply_direction(dir); 134 | ++len_; 135 | tail_ = (tail_ << Setup::kDirBits) | dir; 136 | } 137 | 138 | // Moves the head of the snake to the given direction; then 139 | // moves the second segment of the snake to the space originally 140 | // occupied by the head, the third segment to the space occupied 141 | // by the second, etc. 142 | void move(Direction dir) { 143 | std::copy_backward(&i_[0], &i_[len_ - 1], &i_[len_]); 144 | i_[0] = i_[1] + Setup::apply_direction(dir); 145 | tail_ &= ~(Setup::kDirMask << ((len_ - 2) * Setup::kDirBits)); 146 | tail_ = (tail_ << Setup::kDirBits) | dir; 147 | } 148 | 149 | // Returns the directions the snake has moved in (i==0 is 150 | // the most recent move). May not be called with a number larger 151 | // than len_ - 1. 152 | Direction tail(int i) const { 153 | return Direction((tail_ >> (i * Setup::kDirBits)) & Setup::kDirMask); 154 | } 155 | 156 | // Compares to snakes, based on their location and shape. 157 | bool operator<(const Snake& other) const { 158 | // Doesn't matter which segment gets compared, as long as 159 | // it's consistent. 160 | if (i_[0] != other.i_[0]) { 161 | return i_[0] < other.i_[0]; 162 | } 163 | if (len_ != other.len_) { 164 | return len_ < other.len_; 165 | } 166 | if (tail_ != other.tail_) { 167 | return tail_ < other.tail_; 168 | } 169 | return false; 170 | } 171 | 172 | // Deserializes a snake from bits. 173 | template 174 | void unpack(const P* packer, typename P::Context* pc) { 175 | packer->extract(tail_, kTailBits, pc); 176 | packer->extract(i_[0], Setup::kIndexBits, pc); 177 | packer->extract(len_, Setup::kLenBits, pc); 178 | init_locations_from_tail(); 179 | } 180 | 181 | // Serializes this snake to bits. 182 | template 183 | void pack(P* packer, typename P::Context* pc) const { 184 | packer->deposit(tail_, kTailBits, pc); 185 | packer->deposit(i_[0], Setup::kIndexBits, pc); 186 | packer->deposit(len_, Setup::kLenBits, pc); 187 | } 188 | 189 | // The amount of bits needed to represent a snake in 190 | // this Setup. 191 | static constexpr int packed_width() { 192 | return kTailBits + Setup::kIndexBits + Setup::kLenBits; 193 | } 194 | 195 | // Resets the location of all other segments of the snake to 196 | // be consistent with the shape and the location of the head. 197 | void init_locations_from_tail() { 198 | for (int i = 1; i < len_; ++i) { 199 | i_[i] = i_[i - 1] - Setup::apply_direction(tail(i - 1)); 200 | } 201 | } 202 | 203 | // Moves the snake by the given delta. All segments of the snake 204 | // move by the same amount, so the shape does not change. 205 | void translate(Coord delta) { 206 | for (int i = 0; i < len_; ++i) { 207 | i_[i] += delta; 208 | } 209 | } 210 | 211 | // The last len_ - 1 directions given to move() and grow(). 212 | // Describes the shape of the snake, independent of its 213 | // location. Encoded as two bits per segment (matching the 214 | // Direction enums). The most recent move is encoded in the 215 | // least significant bits. 216 | uint64_t tail_; 217 | // The locations (in the linear coordinate system) of each of the 218 | // snake's segments. 219 | Coord i_[Setup::SnakeMaxLen]; 220 | // The number of segments the snake consists of. Must be at least 2. 221 | int32_t len_; 222 | }; 223 | 224 | // A Gadget is a movable object with a fixed shape. The parts 225 | // of the Gadget do not need to be contiguous. 226 | // 227 | // This class describes the shape and initial location of a Gadget. 228 | // As the Gadget is moved, the movement is tracked as a delta to 229 | // the initial location. 230 | class Gadget { 231 | public: 232 | // Create a Gadget with no parts. 233 | Gadget() : size_(0) { 234 | } 235 | 236 | // Add a new part to the Gadget at the given linear coordinate 237 | // offset compared to the first part. (I.e. the first part should 238 | // always have an offset of 0). 239 | void add(Coord offset) { 240 | i_[size_++] = offset; 241 | } 242 | 243 | // Compare two Gadgets, by shape and location. 244 | bool operator<(const Gadget& other) const { 245 | if (size_ != other.size_) { 246 | return size_ < other.size_; 247 | } 248 | 249 | for (int i = 0; i < size_; ++i) { 250 | if (i_[i] != other.i_[i]) { 251 | return i_[i] < other.i_[i]; 252 | } 253 | } 254 | 255 | // Note: it's important for the State canonicalization 256 | // that initial_offset_ does not affect the sorting. 257 | return false; 258 | } 259 | 260 | // The actual location of the first part of the Gadget in 261 | // the initial setup. 262 | Coord initial_offset_ = 0; 263 | // The number of parts in the gadget. 264 | uint16_t size_; 265 | // The offset from this part to the first part of the Gadget. 266 | Coord i_[8]; 267 | }; 268 | 269 | // The dynamically changing parts of a movable object. 270 | struct GadgetState { 271 | // An index to an array of Gadgets, which corresponds to the 272 | // static setup of this object. 273 | uint16_t template_ ; 274 | // The amount (in the linear coordinate system) by which this 275 | // object has moved after the initial setup. 276 | Coord offset_ = 0; 277 | }; 278 | 279 | // Any data that is immutable based on the scenario description, 280 | // and never changes between States. 281 | // 282 | // - The locations of solid ground, hazards, fruit, and teleporters. 283 | // - The shapes and initial locations of all movable objects. 284 | // 285 | // Also contains state that needs to be copied to the initial 286 | // state: 287 | // 288 | // - The shape and location of each snake. 289 | template 290 | class Map { 291 | public: 292 | using Snake = typename ::Snake; 293 | using Teleporter = typename std::pair; 294 | 295 | // Constructs a Map object from the given base map description. 296 | // [O] is a fruit, [*] is an exit, [T] is a teleporter, [RGB] are 297 | // the heads of snakes; [<>v^] show the shape of the 298 | // snake; [0-9] are the parts of different gadgets, [.] is 299 | // solid ground, [~#] are hazards. 300 | explicit Map(const char* base_map) : exit_(0) { 301 | assert(strlen(base_map) == Setup::MapSize); 302 | base_map_ = new uint8_t[Setup::MapSize]; 303 | int fruit_count = 0; 304 | int snake_count = 0; 305 | int teleporter_count = 0; 306 | int max_len = 0; 307 | std::unordered_map half_teleporter; 308 | for (Coord i = 0; i < Setup::MapSize; ++i) { 309 | const char c = base_map[i]; 310 | if (c == 'O') { 311 | if (Setup::FruitCount) { 312 | fruit_[fruit_count++] = i; 313 | } 314 | base_map_[i] = ' '; 315 | } else if (c == '*') { 316 | assert(!exit_); 317 | base_map_[i] = ' '; 318 | exit_ = i; 319 | } else if (c == 'T') { 320 | if (half_teleporter[c]) { 321 | teleporters_[teleporter_count++] = 322 | std::make_pair(half_teleporter[c], i); 323 | } else { 324 | half_teleporter[c] = i; 325 | } 326 | base_map_[i] = ' '; 327 | } else if (c == 'R' || c == 'G' || c == 'B') { 328 | base_map_[i] = ' '; 329 | Snake snake = Snake(i); 330 | int len = 0; 331 | snake.tail_ = trace_tail(base_map, i, &len); 332 | snake.len_ += len; 333 | snake.init_locations_from_tail(); 334 | snakes_[snake_count++] = snake; 335 | max_len = std::max(max_len, (int) snake.len_); 336 | } else if (isdigit(c)) { 337 | base_map_[i] = ' '; 338 | uint32_t index = c - '0'; 339 | assert(index < Setup::GadgetCount); 340 | if (!gadgets_[index].size_) { 341 | gadgets_[index].initial_offset_ = i; 342 | } 343 | gadgets_[index].add(i - gadgets_[index].initial_offset_); 344 | } else if (c == '>' || c == '<' || c == '^' || c == 'v') { 345 | base_map_[i] = ' '; 346 | } else { 347 | base_map_[i] = c; 348 | } 349 | } 350 | 351 | std::sort(&gadgets_[0], &gadgets_[Setup::GadgetCount]); 352 | 353 | if (Setup::SnakeMaxLen < max_len + Setup::FruitCount) { 354 | fprintf(stderr, "Expected SnakeMaxLen >= %d, got %d\n", 355 | max_len + Setup::FruitCount, 356 | Setup::SnakeMaxLen); 357 | } 358 | assert(fruit_count == Setup::FruitCount); 359 | assert(snake_count == Setup::SnakeCount); 360 | assert(teleporter_count == Setup::TeleporterCount); 361 | assert(exit_); 362 | } 363 | 364 | uint32_t trace_tail(const char* base_map, Coord i, int* len) const { 365 | if (base_map[i - 1] == '>') { 366 | ++*len; 367 | return RIGHT | 368 | (trace_tail(base_map, i - 1, len) << Setup::kDirBits); 369 | } 370 | if (base_map[i + 1] == '<') { 371 | ++*len; 372 | return LEFT | 373 | (trace_tail(base_map, i + 1, len) << Setup::kDirBits); 374 | } 375 | if (base_map[i - Setup::W] == 'v') { 376 | ++*len; 377 | return DOWN | 378 | (trace_tail(base_map, i - Setup::W, len) << Setup::kDirBits); 379 | } 380 | if (base_map[i + Setup::W] == '^') { 381 | ++*len; 382 | return UP | 383 | (trace_tail(base_map, i + Setup::W, len) << Setup::kDirBits); 384 | } 385 | 386 | return 0; 387 | } 388 | 389 | uint8_t operator[](Coord i) const { 390 | return this->base_map_[i]; 391 | } 392 | 393 | uint8_t* base_map_; 394 | // Location of the exit. 395 | Coord exit_; 396 | // Locations of any fruit. 397 | Coord fruit_[Setup::FruitCount]; 398 | // Initial locations and shapes of snakes. 399 | Snake snakes_[Setup::SnakeCount]; 400 | // Initial locations and shapes of Gadgets. 401 | Gadget gadgets_[Setup::GadgetCount]; 402 | // Initial locations of any teleporter pairs. 403 | Teleporter teleporters_[Setup::TeleporterCount]; 404 | }; 405 | 406 | // The serialization of a State object into a bitstream of kWidthBits 407 | // bits (rounded up to the next byte). 408 | // 409 | // All the serialization and deserialization happens with Packer; in 410 | // practice this is just convenience functions for operating on an 411 | // opaque byte array. 412 | template 413 | struct PackedState { 414 | using P = Packer; 415 | 416 | PackedState() {} 417 | PackedState(const State& st) { 418 | typename P::Context pc; 419 | st.pack(&p_, &pc); 420 | p_.flush(&pc); 421 | } 422 | 423 | // The size of the serialized output in bytes. 424 | static constexpr int width_bytes() { return P::Bytes; } 425 | // Returns the i'th element in the serialized state. 426 | uint8_t& at(size_t i) { return p_.bytes_[i]; } 427 | const uint8_t& at(size_t i) const { return p_.bytes_[i]; } 428 | 429 | // Returns the full serialized state. 430 | uint8_t* bytes() { return p_.bytes_; } 431 | const uint8_t* bytes() const { return p_.bytes_; } 432 | 433 | // Computes a hashcode for this state. 434 | uint64_t hash() const { 435 | return CityHash64((char*) bytes(), 436 | width_bytes()); 437 | } 438 | 439 | // Compares two serialized states. 440 | 441 | bool operator==(const PackedState& other) const { 442 | return memcmp(bytes(), other.bytes(), P::Bytes) == 0; 443 | } 444 | 445 | bool operator<(const PackedState& other) const { 446 | // This is morally a memcmp, but it's opencoded since we 447 | // don't care about what the ordering is, just that there 448 | // is one. (The difference is due to endian issues, which 449 | // is why this kind of optimization can't happen automatically). 450 | // 451 | // return memcmp(bytes(), other.bytes(), P::Bytes) < 0; 452 | int i = 0; 453 | for (; i + 7 < P::Bytes; i += 8) { 454 | uint64_t a = *((uint64_t*) (bytes() + i)); 455 | uint64_t b = *((uint64_t*) (other.bytes() + i)); 456 | if (a != b) 457 | return a < b; 458 | } 459 | for (; i + 3 < P::Bytes; i += 4) { 460 | uint32_t a = *((uint32_t*) (bytes() + i)); 461 | uint32_t b = *((uint32_t*) (other.bytes() + i)); 462 | if (a != b) 463 | return a < b; 464 | } 465 | for (; i < P::Bytes; ++i) { 466 | if (at(i) != other.at(i)) { 467 | return at(i) < other.at(i); 468 | } 469 | } 470 | return false; 471 | } 472 | 473 | P p_; 474 | }; 475 | 476 | // An index for all the the mutable parts of a State (Fruits, 477 | // snakes, Gadgets), queryable my map coordinate. 478 | // 479 | // If kDrawTail is true, uses [<>v^] to draw the exact shape of 480 | // snakes rather than just marking the locations of the 481 | // segments. 482 | template 483 | class ObjMap { 484 | public: 485 | using Map = typename State::Map; 486 | using Setup = typename State::Setup; 487 | using Snake = typename State::Snake; 488 | using ObjMask = typename State::ObjMask; 489 | 490 | ObjMap(const State& st, const Map& map) { 491 | draw_objs(st, map, kDrawTail); 492 | } 493 | 494 | // Returns true if no objects overlap the given map coordinate. 495 | bool no_object_at(Coord i) const { 496 | return obj_map_[i] == State::empty_id(); 497 | } 498 | 499 | // Returns true if there is an (uneaten) fruit at the given map 500 | // coordinate. 501 | bool fruit_at(Coord i) const { 502 | return obj_map_[i] == fruit_id(); 503 | } 504 | 505 | // Returns true if the given map coordinate contains an object 506 | // (different from the given object id). 507 | bool foreign_object_at(Coord i, int id) const { 508 | return !no_object_at(i) && 509 | obj_map_[i] != id; 510 | } 511 | 512 | // Returns the id of the object at the given map coordinate. 513 | int id_at(Coord i) const { 514 | return obj_map_[i]; 515 | } 516 | 517 | // If the given map location contains an object, returns 518 | // a bit-mask with the bit corresponding to that object set. 519 | // Otherwise returns 0. 520 | ObjMask mask_at(Coord i) const { 521 | if (id_at(i)) { 522 | return 1 << (id_at(i) - 1); 523 | } 524 | return 0; 525 | } 526 | 527 | int fruit_id() const { return 1 + Setup::ObjCount; } 528 | 529 | private: 530 | void draw_objs(const State& st, 531 | const Map& map, 532 | bool draw_path) { 533 | memset(obj_map_, State::empty_id(), Setup::MapSize); 534 | for (int si = 0; si < Setup::SnakeCount; ++si) { 535 | draw_snake(st, si, draw_path); 536 | } 537 | for (int fi = 0; fi < Setup::FruitCount; ++fi) { 538 | if (st.fruit_active(fi)) { 539 | obj_map_[map.fruit_[fi]] = fruit_id(); 540 | } 541 | } 542 | for (int gi = 0; gi < Setup::GadgetCount; ++gi) { 543 | Coord offset = st.gadgets_[gi].offset_; 544 | if (offset != State::kGadgetDeleted) { 545 | const auto& gadget = map.gadgets_[gi]; 546 | for (int j = 0; j < gadget.size_; ++j) { 547 | obj_map_[offset + gadget.i_[j]] = State::gadget_id(gi); 548 | } 549 | } 550 | } 551 | } 552 | 553 | void draw_snake(const State& st, int si, bool draw_path) { 554 | const Snake& snake = st.snakes_[si]; 555 | int id = State::snake_id(si); 556 | if (draw_path) { 557 | Coord i = snake.i_[0]; 558 | uint64_t tail = snake.tail_; 559 | Direction segment; 560 | for (int j = 0; j < snake.len_; ++j) { 561 | if (j == 0 || !draw_path) { 562 | obj_map_[i] = id; 563 | } else { 564 | switch (segment) { 565 | case UP: obj_map_[i] = '^'; break; 566 | case DOWN: obj_map_[i] = 'v'; break; 567 | case LEFT: obj_map_[i] = '<'; break; 568 | case RIGHT: obj_map_[i] = '>'; break; 569 | } 570 | } 571 | segment = Direction(tail & Setup::kDirMask); 572 | i -= Setup::apply_direction(segment); 573 | tail >>= Setup::kDirBits; 574 | } 575 | } else { 576 | for (int i = 0; i < snake.len_; ++i) { 577 | obj_map_[snake.i_[i]] = id; 578 | } 579 | } 580 | } 581 | 582 | uint8_t obj_map_[Setup::MapSize]; 583 | }; 584 | 585 | template 586 | class State { 587 | using Setup = Setup_; 588 | using Snake = typename ::Snake; 589 | using Teleporter = typename std::pair; 590 | 591 | // The size (in bits) of a serialized state object. 592 | static constexpr int packed_bits() { 593 | return Snake::packed_width() * Setup::SnakeCount + 594 | Setup::FruitCount + 595 | Setup::kIndexBits * Setup::GadgetCount; 596 | } 597 | 598 | public: 599 | using Map = typename ::Map; 600 | using Packed = PackedState; 601 | // A bitmask of objects. Bits (0 : Setup::SnakeCount] are 602 | // the snakes, bits (Setup::SnakeCount : Setup::ObjCount] 603 | // are gadgets. Fruit are not tracked in the mask. 604 | using ObjMask = uint32_t; 605 | 606 | // The null state. 607 | State() { 608 | fruit_ = mask_n_bits(Setup::FruitCount); 609 | for (int gi = 0; gi < Setup::GadgetCount; ++gi) { 610 | gadgets_[gi].template_ = gi; 611 | } 612 | } 613 | 614 | // The initial state. 615 | State(const Map& map) : State() { 616 | for (int si = 0; si < Setup::SnakeCount; ++si) { 617 | snakes_[si] = map.snakes_[si]; 618 | } 619 | for (int gi = 0; gi < Setup::GadgetCount; ++gi) { 620 | gadgets_[gi].offset_ = map.gadgets_[gi].initial_offset_; 621 | gadgets_[gi].template_ = gi; 622 | } 623 | } 624 | 625 | // De-serializes a state from bytes. 626 | State(const Packed& p) : State() { 627 | typename Packed::P::Context pc; 628 | unpack(&p.p_, &pc); 629 | }; 630 | 631 | // Calls fun on states that can be directly reached from this 632 | // state. Returns true immediately if fun returns true. Otherwise 633 | // returns false. 634 | bool do_valid_moves(const Map& map, 635 | std::function fun) const { 636 | static Direction dirs[] = { 637 | UP, RIGHT, DOWN, LEFT, 638 | }; 639 | ObjMap obj_map(*this, map); 640 | // Which objects overlap a teleporter before the move? (Needed 641 | // since teleporters behave as edge triggered). 642 | ObjMask tele_mask = teleporter_overlap(map, obj_map); 643 | for (int si = 0; si < Setup::SnakeCount; ++si) { 644 | if (!snakes_[si].len_) { 645 | // This snake has already exited the level, can't move. 646 | continue; 647 | } 648 | // Make an ObjMap that's just like obj_map, but doesn't 649 | // have the current snake's tail filled in. (Needed since 650 | // the rules for how the tail is handled for movement vs 651 | // pushing are different). 652 | State push_st(*this); 653 | push_st.snakes_[si].len_--; 654 | ObjMap push_map(push_st, map); 655 | 656 | for (auto dir : dirs) { 657 | // See what would happen when you move the current 658 | // snake in each of the directions. (Grow snake, move 659 | // snake to an empty space , or push one or more 660 | // objects). 661 | Coord delta = Setup::apply_direction(dir); 662 | Coord to = snakes_[si].i_[0] + delta; 663 | ObjMask pushed_ids = 0; 664 | int fruit_index = 0; 665 | if (is_valid_grow(map, obj_map, to, &fruit_index)) { 666 | State new_state(*this); 667 | new_state.snakes_[si].grow(dir); 668 | new_state.delete_fruit(fruit_index); 669 | if (new_state.process_gravity(map, tele_mask)) { 670 | new_state.canonicalize(map); 671 | if (fun(new_state)) { 672 | return true; 673 | } 674 | } 675 | } if (is_valid_move(map, obj_map, to)) { 676 | State new_state(*this); 677 | new_state.snakes_[si].move(dir); 678 | if (new_state.process_gravity(map, tele_mask)) { 679 | new_state.canonicalize(map); 680 | if (fun(new_state)) { 681 | return true; 682 | } 683 | } 684 | } else if (is_valid_push(map, push_map, 685 | snake_id(si), 686 | snakes_[si].i_[0], 687 | delta, 688 | &pushed_ids)) { 689 | State new_state(*this); 690 | new_state.snakes_[si].move(dir); 691 | new_state.do_pushes(obj_map, pushed_ids, delta); 692 | if (new_state.process_gravity(map, tele_mask)) { 693 | new_state.canonicalize(map); 694 | if (fun(new_state)) { 695 | return true; 696 | } 697 | } 698 | } 699 | } 700 | } 701 | return false; 702 | } 703 | 704 | // Returns true if the state has reached the win condition. 705 | // (I.e. all Snakes have exited the level). 706 | bool win() { 707 | for (int si = 0; si < Setup::SnakeCount; ++si) { 708 | if (snakes_[si].len_) { 709 | return false; 710 | } 711 | } 712 | 713 | return true; 714 | } 715 | 716 | // Prints the game state to stdout. 717 | void print(const Map& map) const { 718 | ObjMap obj_map(*this, map); 719 | 720 | for (Coord i = 0; i < Setup::H; ++i) { 721 | for (Coord j = 0; j < Setup::W; ++j) { 722 | Coord l = i * Setup::W + j; 723 | bool teleport = false; 724 | for (auto t : map.teleporters_) { 725 | if (t.first == l || t.second == l) { 726 | teleport = true; 727 | } 728 | } 729 | if (!obj_map.no_object_at(l)) { 730 | int id = obj_map.id_at(l); 731 | char c; 732 | if (id < (Setup::SnakeCount + 1)) { 733 | c = 'A' + (id - 1); 734 | } else if (id < Setup::ObjCount + 1) { 735 | c = '0' + (id - 1 - Setup::SnakeCount); 736 | } else if (id == obj_map.fruit_id()) { 737 | c = 'Q'; 738 | } else { 739 | c = id; 740 | } 741 | printf("%c", c); 742 | } else if (l == map.exit_) { 743 | printf("*"); 744 | } else if (teleport) { 745 | printf("X"); 746 | } else { 747 | printf("%c", map[l]); 748 | } 749 | } 750 | printf("\n"); 751 | } 752 | printf("\n"); 753 | } 754 | 755 | private: 756 | friend Packed; 757 | friend ObjMap; 758 | friend ObjMap; 759 | 760 | static const uint16_t kGadgetDeleted = 0; 761 | 762 | static int empty_id() { return 0; } 763 | static int snake_id(int si) { return (1 + si); } 764 | static int gadget_id(int i) { return (1 + i + Setup::SnakeCount); } 765 | 766 | static ObjMask snake_mask(int si) { return 1 << si; } 767 | static ObjMask gadget_mask(int i) { return 1 << (Setup::SnakeCount + i); } 768 | 769 | // Returns true if the i'th fruit is still unconsumed. 770 | bool fruit_active(int fi) const { 771 | return (fruit_ & (1 << fi)) != 0; 772 | } 773 | 774 | // Mark the i'th fruit as consumed. 775 | void delete_fruit(int fi) { 776 | fruit_ = fruit_ & ~(1 << fi); 777 | } 778 | 779 | // Returns a mask of all objects that currently overlap a 780 | // teleporter. Each object will have one bit set for each 781 | // half of each teleporter-pair. 782 | ObjMask teleporter_overlap(const Map& map, 783 | const ObjMap& objmap) const { 784 | ObjMask mask = 0; 785 | const uint32_t width = Setup::ObjCount; 786 | for (int ti = 0; ti < Setup::TeleporterCount; ++ti) { 787 | mask |= 788 | ((objmap.mask_at(map.teleporters_[ti].first)) | 789 | (objmap.mask_at(map.teleporters_[ti].second) << width)) 790 | << (width * 2 * ti); 791 | } 792 | return mask; 793 | } 794 | 795 | // Returns true if a snake could move to the given location 796 | // without pushing anything. 797 | bool is_valid_move(const Map& map, 798 | const ObjMap& obj_map, 799 | Coord to) const { 800 | if (obj_map.no_object_at(to) && empty_terrain_at(map, to)) { 801 | return true; 802 | } 803 | 804 | return false; 805 | } 806 | 807 | bool empty_terrain_at(const Map& map, Coord i) const { 808 | return map[i] == ' '; 809 | } 810 | 811 | // Returns true if a snake could grow by moving to the given 812 | // location. If that's the case, sets fruit_index to the 813 | // index of the fruit that would be eaten. 814 | bool is_valid_grow(const Map& map, 815 | const ObjMap& obj_map, 816 | Coord to, 817 | int* fruit_index) const { 818 | if (!obj_map.fruit_at(to)) { 819 | return false; 820 | } 821 | 822 | for (int fi = 0; fi < Setup::FruitCount; ++fi) { 823 | Coord fruit = map.fruit_[fi]; 824 | if (fruit_active(fi) && fruit == to) { 825 | *fruit_index = fi; 826 | return true; 827 | } 828 | } 829 | 830 | assert(false); 831 | } 832 | 833 | // Returns true if the snake at the index pusher_id could push 834 | // an object at location push_at (with delta being the push 835 | // direction). Otherwise returns false. 836 | // 837 | // The bit corresponding to each pushed object will be toggled on 838 | // in pushed_ids. 839 | bool is_valid_push(const Map& map, 840 | const ObjMap& obj_map, 841 | int pusher_id, 842 | Coord push_at, 843 | Coord delta, 844 | ObjMask* pushed_ids) const __attribute__((noinline)) { 845 | Coord to = push_at + delta; 846 | 847 | if (// Nothing to push 848 | obj_map.no_object_at(to) || 849 | // Object can't push itself. 850 | obj_map.id_at(to) == pusher_id || 851 | // Can't push fruit. 852 | obj_map.fruit_at(to)) { 853 | return false; 854 | } 855 | 856 | // Initialize pushed_ids with the first candidate object 857 | // to push. 858 | *pushed_ids = obj_map.mask_at(to); 859 | bool again = true; 860 | 861 | // If object X is being pushed in direction D, and doing 862 | // that would require pushing object Y too, add Y to 863 | // the set as well. Repeat until no more objects get added 864 | // to the set during an iteration. 865 | // 866 | // If it turns out that one of the objects in the set is 867 | // not pushable, return false since the set as a whole can't 868 | // move. 869 | while (again) { 870 | again = false; 871 | for (int si = 0; si < Setup::SnakeCount; ++si) { 872 | if (*pushed_ids & snake_mask(si)) { 873 | ObjMask new_pushed_ids = 0; 874 | if (!snake_can_be_pushed(map, obj_map, 875 | si, 876 | delta, 877 | &new_pushed_ids)) { 878 | return false; 879 | } 880 | if (new_pushed_ids & ~(*pushed_ids)) { 881 | *pushed_ids |= new_pushed_ids; 882 | again = true; 883 | } 884 | } 885 | } 886 | for (int gi = 0; gi < Setup::GadgetCount; ++gi) { 887 | if (*pushed_ids & gadget_mask(gi)) { 888 | ObjMask new_pushed_ids = 0; 889 | if (!gadget_can_be_pushed(map, 890 | obj_map, 891 | gi, 892 | delta, 893 | &new_pushed_ids)) { 894 | return false; 895 | } 896 | if (new_pushed_ids & ~(*pushed_ids)) { 897 | *pushed_ids |= new_pushed_ids; 898 | again = true; 899 | } 900 | } 901 | } 902 | } 903 | 904 | // Don't allow the initiating object to push itself. 905 | return !(*pushed_ids & snake_mask(pusher_id - 1)); 906 | } 907 | 908 | // Returns false if the snake at index si definitely can't be 909 | // pushed in the direction of delta. If pushing the object would 910 | // require pushing another object, mark that object in the 911 | // pushed_ids mask. 912 | bool snake_can_be_pushed(const Map& map, 913 | const ObjMap& obj_map, 914 | int si, 915 | Coord delta, 916 | ObjMask* pushed_ids) const __attribute__((noinline)) { 917 | const Snake& snake = snakes_[si]; 918 | 919 | for (int i = 0; i < snake.len_; ++i) { 920 | // The space the Snake's head would be pushed to. 921 | Coord to = snake.i_[i] + delta; 922 | if (!empty_terrain_at(map, to)) { 923 | return false; 924 | } 925 | if (obj_map.fruit_at(to)) { 926 | return false; 927 | } 928 | if (obj_map.foreign_object_at(to, snake_id(si))) { 929 | *pushed_ids |= obj_map.mask_at(to); 930 | } 931 | } 932 | 933 | return true; 934 | } 935 | 936 | // Returns false if the gadget at index gi definitely can't be 937 | // pushed in the direction of delta. If pushing the object would 938 | // require pushing another object, mark that object in the 939 | // pushed_ids mask. 940 | bool gadget_can_be_pushed(const Map& map, 941 | const ObjMap& obj_map, 942 | int gi, 943 | Coord delta, 944 | ObjMask* pushed_ids) const __attribute__((noinline)) { 945 | const auto& gadget = map.gadgets_[gi]; 946 | Coord offset = gadgets_[gi].offset_; 947 | 948 | for (int j = 0; j < gadget.size_; ++j) { 949 | Coord i = gadget.i_[j] + offset + delta; 950 | if (!empty_terrain_at(map, i)) { 951 | return false; 952 | } 953 | if (obj_map.fruit_at(i)) { 954 | return false; 955 | } 956 | if (!obj_map.no_object_at(i)) { 957 | *pushed_ids |= obj_map.mask_at(i); 958 | } 959 | } 960 | 961 | return true; 962 | } 963 | 964 | // Move all objects that are toggled in pushed_ids in the 965 | // direction push_delta. 966 | void do_pushes(const ObjMap& obj_map, 967 | ObjMask pushed_ids, Coord push_delta) { 968 | for (int si = 0; si < Setup::SnakeCount; ++si) { 969 | if (pushed_ids & snake_mask(si)) { 970 | snakes_[si].translate(push_delta); 971 | } 972 | } 973 | for (int gi = 0; gi < Setup::GadgetCount; ++gi) { 974 | if (pushed_ids & gadget_mask(gi)) { 975 | gadgets_[gi].offset_ += push_delta; 976 | } 977 | } 978 | } 979 | 980 | // Process the "physics", iteratively until there are no more 981 | // changes. 982 | // 983 | // - Snakes that overlap an exit leave the map. 984 | // - Objects that overlap a teleporter they didn't overlap 985 | // previously will teleport. 986 | // - Objects that are not supported by the ground or a fruit 987 | // will drop down a state. 988 | bool process_gravity(const Map& map, ObjMask orig_tele_mask) 989 | __attribute__((noinline)) { 990 | // A mask with a bit set for each object that might still 991 | // be falling. Once an object reaches terra firma, it's 992 | // bit is set to zero and it can be ignored on further 993 | // iterations. 994 | ObjMask recompute_falling = mask_n_bits(Setup::ObjCount); 995 | // For each object O, contains a mask of the objects O' that 996 | // are supporting this object. (I.e. O can only be falling 997 | // if O' is also falling). 998 | ObjMask falling[Setup::ObjCount]; 999 | 1000 | again: 1001 | // FIXME. Figure out if exits and teleporters have different 1002 | // priority. Is it possible to construct a case where that 1003 | // matters? 1004 | check_exits(map); 1005 | // FIXME: The teleporter + gravity interaction doesn't quite 1006 | // match the actual game. There you can have the scenario where 1007 | // snake A supports snake B. Then: 1008 | // 1. A moves, and goes through a teleporter 1009 | // 2. Both A and B drop due to gravity 1010 | // 3. B is now on a teleporter. The remote side is not blocked 1011 | // by A. But B does not teleport. 1012 | // Constructing more exact test cases is proving tricky. 1013 | ObjMap obj_map(*this, map); 1014 | ObjMask new_tele_mask = teleporter_overlap(map, obj_map); 1015 | if (new_tele_mask & ~orig_tele_mask) { 1016 | if (process_teleports(map, obj_map, orig_tele_mask, 1017 | new_tele_mask)) { 1018 | orig_tele_mask = teleporter_overlap(map, 1019 | ObjMap(*this, map)); 1020 | goto again; 1021 | } 1022 | } 1023 | orig_tele_mask = new_tele_mask; 1024 | 1025 | // Recompute the supports for each object. 1026 | // 1027 | // supported will be set to 1 for any objects that are now on 1028 | // the ground. For objects that aren't, falling will be 1029 | // updated with a new mask of possibly supporting objects. 1030 | ObjMask supported = 0; 1031 | for (int si = 0; si < Setup::SnakeCount; ++si) { 1032 | ObjMask mask = 0; 1033 | if (snakes_[si].len_) { 1034 | if (recompute_falling & snake_mask(si)) { 1035 | mask = is_snake_falling(map, obj_map, si); 1036 | } 1037 | } 1038 | falling[si] = mask; 1039 | if (!mask) { 1040 | supported |= snake_mask(si); 1041 | } 1042 | } 1043 | for (int gi = 0; gi < Setup::GadgetCount; ++gi) { 1044 | Coord offset = gadgets_[gi].offset_; 1045 | ObjMask mask = 0; 1046 | if (offset != kGadgetDeleted) { 1047 | if (recompute_falling & 1048 | gadget_mask(gi)) { 1049 | mask = is_gadget_falling(map, obj_map, gi); 1050 | } 1051 | } 1052 | falling[Setup::SnakeCount + gi] = mask; 1053 | if (!mask) { 1054 | supported |= gadget_mask(gi); 1055 | } 1056 | } 1057 | recompute_falling = 0; 1058 | 1059 | // Expand the supported set: for any object O that is not 1060 | // supported yet, check if any of its supporting objects O' 1061 | // are in the supported set. If at least one is, add O to 1062 | // the supported set. 1063 | // 1064 | // Iterate until no more objects are being added to the set. 1065 | while (1) { 1066 | bool again = false; 1067 | for (int i = 0; i < Setup::ObjCount; ++i) { 1068 | ObjMask mask = 1 << i; 1069 | if (!(supported & mask) && 1070 | (supported & falling[i])) { 1071 | supported |= mask; 1072 | again = true; 1073 | } 1074 | } 1075 | if (!again) { 1076 | break; 1077 | } 1078 | } 1079 | 1080 | // Anything not in the supported set gets pushed down one 1081 | // step. 1082 | ObjMask to_push = mask_n_bits(Setup::ObjCount) 1083 | & ~supported; 1084 | 1085 | if (to_push) { 1086 | do_pushes(obj_map, to_push, Setup::W); 1087 | // If the object dropping into the hazard would cause 1088 | // a game over situation, bail out. 1089 | if (destroy_if_intersects_hazard(map, obj_map, to_push)) { 1090 | return false; 1091 | } 1092 | // Recompute the situation for the objects that fell down 1093 | // this turn. 1094 | recompute_falling |= to_push; 1095 | goto again; 1096 | } 1097 | 1098 | return true; 1099 | } 1100 | 1101 | // For each object that's just moved to a teleporter (i.e. is 1102 | // in new_tele_mask but not in orig_tele_mask), move the 1103 | // object to the other side if possible. 1104 | // 1105 | // Returns true if any objects were teleported. 1106 | bool process_teleports(const Map& map, const ObjMap& obj_map, 1107 | ObjMask orig_tele_mask, 1108 | ObjMask new_tele_mask) { 1109 | ObjMask only_new = new_tele_mask & ~orig_tele_mask; 1110 | ObjMask test = 1; 1111 | bool teleported = false; 1112 | // This is over-engineered for the possibility of multiple 1113 | // teleporters. But those don't actually appear in the game, 1114 | // and there are some interesting semantic problems with them 1115 | // with two different teleporter pairs being triggered at the 1116 | // same time, so it's just a guess that this is how they'd 1117 | // work. 1118 | for (int ti = 0; ti < Setup::TeleporterCount; ++ti) { 1119 | Coord delta = map.teleporters_[ti].second - 1120 | map.teleporters_[ti].first; 1121 | for (int dir = 0; dir < 2; ++dir) { 1122 | for (int si = 0; si < Setup::SnakeCount; ++si) { 1123 | if (test & only_new) { 1124 | if (try_snake_teleport(map, obj_map, si, delta)) { 1125 | teleported = true; 1126 | } 1127 | } 1128 | test <<= 1; 1129 | } 1130 | for (int gi = 0; gi < Setup::GadgetCount; ++gi) { 1131 | if (test & only_new) { 1132 | if (try_gadget_teleport(map, obj_map, gi, delta)) { 1133 | teleported = true; 1134 | } 1135 | } 1136 | test <<= 1; 1137 | } 1138 | // Delta was from A to B, just negate it for 1139 | // handling the B to A case. 1140 | delta = -delta; 1141 | } 1142 | } 1143 | 1144 | return teleported; 1145 | } 1146 | 1147 | // Check if the snake would fit on the map if it were moved by the 1148 | // given delta. If so, move it and return true. Otherwise return 1149 | // false. 1150 | bool try_snake_teleport(const Map& map, 1151 | const ObjMap& obj_map, 1152 | int si, Coord delta) { 1153 | const Snake& snake = snakes_[si]; 1154 | 1155 | for (int i = 0; i < snake.len_; ++i) { 1156 | Coord to = snake.i_[i] + delta; 1157 | if (map[to] != ' ') { 1158 | return false; 1159 | } 1160 | // If segment X of a snake would teleport into the space 1161 | // occupied by segment Y of the same snake pre-teleport, 1162 | // is the teleport blocked? I'm assuming yes. If not, 1163 | // this should be a foreign_object_at instead. 1164 | if (!obj_map.no_object_at(to)) { 1165 | return false; 1166 | } 1167 | } 1168 | 1169 | snakes_[si].translate(delta); 1170 | 1171 | return true; 1172 | } 1173 | 1174 | // Check if the gadget would fit on the map if it were moved by 1175 | // the given delta. If so, move it and return true. Otherwise 1176 | // return false. 1177 | bool try_gadget_teleport(const Map& map, 1178 | const ObjMap& obj_map, 1179 | int gi, 1180 | Coord delta) { 1181 | const auto& gadget = map.gadgets_[gi]; 1182 | Coord offset = gadgets_[gi].offset_ + delta; 1183 | 1184 | for (int j = 0; j < gadget.size_; ++j) { 1185 | Coord to = gadget.i_[j] + offset; 1186 | if (map[to] != ' ') { 1187 | return false; 1188 | } 1189 | if (!obj_map.no_object_at(to)) { 1190 | return false; 1191 | } 1192 | } 1193 | 1194 | // There's a funny thing here where a sparse gadget could 1195 | // theoretically teleport halfway over a map edge, since 1196 | // the solid border tile protection doesn't work there. 1197 | // But if a solution ended up abusing that, it'd be easy to 1198 | // fix by just adding more padding to the map. 1199 | 1200 | gadgets_[gi].offset_ += delta; 1201 | return true; 1202 | } 1203 | 1204 | // Check whether any of the objects whose bits are set in the 1205 | // pushed_ids mask is intersecting a hazard. 1206 | // 1207 | // - A snake intersecting a hazard is a game over. 1208 | // - A gadget intersecting a hazard just gets destroyed. 1209 | // 1210 | // Returns true iff game over. 1211 | bool destroy_if_intersects_hazard(const Map& map, 1212 | const ObjMap& obj_map, 1213 | ObjMask pushed_ids) { 1214 | for (int si = 0; si < Setup::SnakeCount; ++si) { 1215 | if (pushed_ids & snake_mask(si)) { 1216 | if (snake_intersects_hazard(map, snakes_[si])) 1217 | return true; 1218 | } 1219 | } 1220 | for (int gi = 0; gi < Setup::GadgetCount; ++gi) { 1221 | if (pushed_ids & gadget_mask(gi)) { 1222 | if (gadget_intersects_hazard(map, gi)) { 1223 | gadgets_[gi].offset_ = kGadgetDeleted; 1224 | } 1225 | } 1226 | } 1227 | return false; 1228 | } 1229 | 1230 | // It is possible for two states to be functionally equal but 1231 | // have a different internal representation. This happens 1232 | // since two snakes of the same size, or two gadgets of the 1233 | // same shape are interchangable. Their exact identity never 1234 | // matters for the solution. 1235 | // 1236 | // Transforms the state (in-place) such that if S and S' are 1237 | // functionally equal but S != S', canonicalize(S) == 1238 | // canonicalize(S'). 1239 | void canonicalize(const Map& map) { 1240 | // Sort the snakefs. 1241 | std::sort(&snakes_[0], &snakes_[Setup::SnakeCount]); 1242 | if (Setup::GadgetCount > 0) { 1243 | // Sort the gadgets primarily by shape, secondarily 1244 | // by offset. 1245 | std::sort(&gadgets_[0], &gadgets_[Setup::GadgetCount], 1246 | [&map] (const GadgetState& a, const GadgetState& b) { 1247 | const Gadget& ag = map.gadgets_[a.template_]; 1248 | const Gadget& bg = map.gadgets_[b.template_]; 1249 | if (ag < bg) { 1250 | return true; 1251 | } 1252 | if (bg < ag) { 1253 | return false; 1254 | } 1255 | return a.offset_ < b.offset_; 1256 | }); 1257 | } 1258 | } 1259 | 1260 | // Checks whether any snakes are in a position to exit the map. 1261 | // If so, marks them as having exited by setting the snake's 1262 | // length to 0. 1263 | void check_exits(const Map& map) { 1264 | if (fruit_) { 1265 | // Can't use exits until all fruit are eaten. 1266 | return; 1267 | } 1268 | 1269 | for (auto& snake : snakes_) { 1270 | if (snake.len_) { 1271 | if (snake_head_at_exit(map, snake)) { 1272 | snake.len_ = 0; 1273 | snake.i_[0] = 0; 1274 | snake.tail_ = 0; 1275 | } 1276 | } 1277 | } 1278 | } 1279 | 1280 | // If the snake is supported by the ground, returns zero. 1281 | // Otherwise returns a bitmask with 1 set for each object 1282 | // that might be supporting the snake + the snake itself. 1283 | ObjMask is_snake_falling(const Map& map, 1284 | const ObjMap& obj_map, 1285 | int si) const { 1286 | const Snake& snake = snakes_[si]; 1287 | ObjMask pushed_ids = snake_mask(si); 1288 | 1289 | for (int i = 0; i < snake.len_; ++i) { 1290 | Coord below = snake.i_[i] + Setup::W; 1291 | if (map[below] == '.' || 1292 | obj_map.fruit_at(below)) { 1293 | return 0; 1294 | } 1295 | if (obj_map.foreign_object_at(below, snake_id(si))) { 1296 | pushed_ids |= obj_map.mask_at(below); 1297 | } 1298 | } 1299 | 1300 | return pushed_ids; 1301 | } 1302 | 1303 | // If the gadget is supported by the ground or a spike, returns 1304 | // zero. Otherwise returns a bitmask with 1 set for each object 1305 | // that might be supporting the gadget + the gadget itself. 1306 | ObjMask is_gadget_falling(const Map& map, 1307 | const ObjMap& obj_map, 1308 | int gi) const { 1309 | const auto& gadget = map.gadgets_[gi]; 1310 | 1311 | ObjMask pushed_ids = gadget_mask(gi); 1312 | int id = gadget_id(gi); 1313 | 1314 | for (int j = 0; j < gadget.size_; ++j) { 1315 | Coord at = gadget.i_[j] + gadgets_[gi].offset_; 1316 | Coord below = at + Setup::W; 1317 | if (map[below] == '.' || 1318 | map[below] == '#' || 1319 | obj_map.fruit_at(below)) { 1320 | return 0; 1321 | } 1322 | if (obj_map.foreign_object_at(below, id)) { 1323 | pushed_ids |= obj_map.mask_at(below); 1324 | } 1325 | } 1326 | 1327 | return pushed_ids; 1328 | } 1329 | 1330 | bool snake_head_at_exit(const Map& map, const Snake& snake) const { 1331 | // Only the head of the snake will trigger an exit 1332 | return snake.i_[0] == map.exit_; 1333 | } 1334 | 1335 | // Returns true if a segment of the snake is located in a spike or 1336 | // a water location. 1337 | bool snake_intersects_hazard(const Map& map, const Snake& snake) const { 1338 | for (int i = 0; i < snake.len_; ++i) { 1339 | Coord at = snake.i_[i]; 1340 | if (map[at] == '~' || map[at] == '#') 1341 | return true; 1342 | } 1343 | 1344 | return false; 1345 | } 1346 | 1347 | // Returns true if a part of the gadget is located in a water 1348 | // location. 1349 | bool gadget_intersects_hazard(const Map& map, 1350 | int gi) const { 1351 | Coord offset = gadgets_[gi].offset_; 1352 | if (offset == kGadgetDeleted) 1353 | return false; 1354 | const auto& gadget = map.gadgets_[gi]; 1355 | for (int j = 0; j < gadget.size_; ++j) { 1356 | // Spikes aren't a hazard for gadgets. 1357 | if (map[gadget.i_[j] + offset] == '~') 1358 | return true; 1359 | } 1360 | 1361 | return false; 1362 | } 1363 | 1364 | // De-serialize the state. 1365 | template 1366 | void unpack(const P* packer, typename P::Context* pc) { 1367 | for (int si = 0; si < Setup::SnakeCount; ++si) { 1368 | snakes_[si].unpack(packer, pc); 1369 | } 1370 | packer->extract(fruit_, Setup::FruitCount, pc); 1371 | for (int gi = 0 ; gi < Setup::GadgetCount; ++gi) { 1372 | packer->extract(gadgets_[gi].offset_, 1373 | Setup::kIndexBits, 1374 | pc); 1375 | } 1376 | } 1377 | 1378 | // Serialize the state. 1379 | template 1380 | void pack(P* packer, typename P::Context* pc) const { 1381 | for (int si = 0; si < Setup::SnakeCount; ++si) { 1382 | snakes_[si].pack(packer, pc); 1383 | } 1384 | packer->deposit(fruit_, Setup::FruitCount, pc); 1385 | for (int gi = 0 ; gi < Setup::GadgetCount; ++gi) { 1386 | packer->deposit(gadgets_[gi].offset_, 1387 | Setup::kIndexBits, 1388 | pc); 1389 | } 1390 | } 1391 | 1392 | Snake snakes_[Setup::SnakeCount]; 1393 | GadgetState gadgets_[Setup::GadgetCount]; 1394 | // Bitmask, fruit that are still on the map have the 1 bit set. 1395 | uint64_t fruit_; 1396 | }; 1397 | 1398 | #endif // GAME_H 1399 | -------------------------------------------------------------------------------- /src/third-party/cityhash/city.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Google, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | // 21 | // CityHash, by Geoff Pike and Jyrki Alakuijala 22 | // 23 | // This file provides CityHash64() and related functions. 24 | // 25 | // It's probably possible to create even faster hash functions by 26 | // writing a program that systematically explores some of the space of 27 | // possible hash functions, by using SIMD instructions, or by 28 | // compromising on hash quality. 29 | 30 | // #include "config.h" 31 | #include "city.h" 32 | 33 | #include 34 | #include // for memcpy and memset 35 | 36 | using namespace std; 37 | 38 | static uint64 UNALIGNED_LOAD64(const char *p) { 39 | uint64 result; 40 | memcpy(&result, p, sizeof(result)); 41 | return result; 42 | } 43 | 44 | static uint32 UNALIGNED_LOAD32(const char *p) { 45 | uint32 result; 46 | memcpy(&result, p, sizeof(result)); 47 | return result; 48 | } 49 | 50 | #ifdef _MSC_VER 51 | 52 | #include 53 | #define bswap_32(x) _byteswap_ulong(x) 54 | #define bswap_64(x) _byteswap_uint64(x) 55 | 56 | #elif defined(__APPLE__) 57 | 58 | // Mac OS X / Darwin features 59 | #include 60 | #define bswap_32(x) OSSwapInt32(x) 61 | #define bswap_64(x) OSSwapInt64(x) 62 | 63 | #elif defined(__NetBSD__) 64 | 65 | #include 66 | #include 67 | #if defined(__BSWAP_RENAME) && !defined(__bswap_32) 68 | #define bswap_32(x) bswap32(x) 69 | #define bswap_64(x) bswap64(x) 70 | #endif 71 | 72 | #else 73 | 74 | #include 75 | 76 | #endif 77 | 78 | #ifdef WORDS_BIGENDIAN 79 | #define uint32_in_expected_order(x) (bswap_32(x)) 80 | #define uint64_in_expected_order(x) (bswap_64(x)) 81 | #else 82 | #define uint32_in_expected_order(x) (x) 83 | #define uint64_in_expected_order(x) (x) 84 | #endif 85 | 86 | #if !defined(LIKELY) 87 | #if HAVE_BUILTIN_EXPECT 88 | #define LIKELY(x) (__builtin_expect(!!(x), 1)) 89 | #else 90 | #define LIKELY(x) (x) 91 | #endif 92 | #endif 93 | 94 | static uint64 Fetch64(const char *p) { 95 | return uint64_in_expected_order(UNALIGNED_LOAD64(p)); 96 | } 97 | 98 | static uint32 Fetch32(const char *p) { 99 | return uint32_in_expected_order(UNALIGNED_LOAD32(p)); 100 | } 101 | 102 | // Some primes between 2^63 and 2^64 for various uses. 103 | static const uint64 k0 = 0xc3a5c85c97cb3127ULL; 104 | static const uint64 k1 = 0xb492b66fbe98f273ULL; 105 | static const uint64 k2 = 0x9ae16a3b2f90404fULL; 106 | 107 | // Magic numbers for 32-bit hashing. Copied from Murmur3. 108 | static const uint32_t c1 = 0xcc9e2d51; 109 | static const uint32_t c2 = 0x1b873593; 110 | 111 | // A 32-bit to 32-bit integer hash copied from Murmur3. 112 | static uint32 fmix(uint32 h) 113 | { 114 | h ^= h >> 16; 115 | h *= 0x85ebca6b; 116 | h ^= h >> 13; 117 | h *= 0xc2b2ae35; 118 | h ^= h >> 16; 119 | return h; 120 | } 121 | 122 | static uint32 Rotate32(uint32 val, int shift) { 123 | // Avoid shifting by 32: doing so yields an undefined result. 124 | return shift == 0 ? val : ((val >> shift) | (val << (32 - shift))); 125 | } 126 | 127 | #undef PERMUTE3 128 | #define PERMUTE3(a, b, c) do { std::swap(a, b); std::swap(a, c); } while (0) 129 | 130 | static uint32 Mur(uint32 a, uint32 h) { 131 | // Helper from Murmur3 for combining two 32-bit values. 132 | a *= c1; 133 | a = Rotate32(a, 17); 134 | a *= c2; 135 | h ^= a; 136 | h = Rotate32(h, 19); 137 | return h * 5 + 0xe6546b64; 138 | } 139 | 140 | static uint32 Hash32Len13to24(const char *s, size_t len) { 141 | uint32 a = Fetch32(s - 4 + (len >> 1)); 142 | uint32 b = Fetch32(s + 4); 143 | uint32 c = Fetch32(s + len - 8); 144 | uint32 d = Fetch32(s + (len >> 1)); 145 | uint32 e = Fetch32(s); 146 | uint32 f = Fetch32(s + len - 4); 147 | uint32 h = len; 148 | 149 | return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h))))))); 150 | } 151 | 152 | static uint32 Hash32Len0to4(const char *s, size_t len) { 153 | uint32 b = 0; 154 | uint32 c = 9; 155 | for (int i = 0; i < len; i++) { 156 | signed char v = s[i]; 157 | b = b * c1 + v; 158 | c ^= b; 159 | } 160 | return fmix(Mur(b, Mur(len, c))); 161 | } 162 | 163 | static uint32 Hash32Len5to12(const char *s, size_t len) { 164 | uint32 a = len, b = len * 5, c = 9, d = b; 165 | a += Fetch32(s); 166 | b += Fetch32(s + len - 4); 167 | c += Fetch32(s + ((len >> 1) & 4)); 168 | return fmix(Mur(c, Mur(b, Mur(a, d)))); 169 | } 170 | 171 | uint32 CityHash32(const char *s, size_t len) { 172 | if (len <= 24) { 173 | return len <= 12 ? 174 | (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) : 175 | Hash32Len13to24(s, len); 176 | } 177 | 178 | // len > 24 179 | uint32 h = len, g = c1 * len, f = g; 180 | uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2; 181 | uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2; 182 | uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2; 183 | uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2; 184 | uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2; 185 | h ^= a0; 186 | h = Rotate32(h, 19); 187 | h = h * 5 + 0xe6546b64; 188 | h ^= a2; 189 | h = Rotate32(h, 19); 190 | h = h * 5 + 0xe6546b64; 191 | g ^= a1; 192 | g = Rotate32(g, 19); 193 | g = g * 5 + 0xe6546b64; 194 | g ^= a3; 195 | g = Rotate32(g, 19); 196 | g = g * 5 + 0xe6546b64; 197 | f += a4; 198 | f = Rotate32(f, 19); 199 | f = f * 5 + 0xe6546b64; 200 | size_t iters = (len - 1) / 20; 201 | do { 202 | uint32 a0 = Rotate32(Fetch32(s) * c1, 17) * c2; 203 | uint32 a1 = Fetch32(s + 4); 204 | uint32 a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2; 205 | uint32 a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2; 206 | uint32 a4 = Fetch32(s + 16); 207 | h ^= a0; 208 | h = Rotate32(h, 18); 209 | h = h * 5 + 0xe6546b64; 210 | f += a1; 211 | f = Rotate32(f, 19); 212 | f = f * c1; 213 | g += a2; 214 | g = Rotate32(g, 18); 215 | g = g * 5 + 0xe6546b64; 216 | h ^= a3 + a1; 217 | h = Rotate32(h, 19); 218 | h = h * 5 + 0xe6546b64; 219 | g ^= a4; 220 | g = bswap_32(g) * 5; 221 | h += a4 * 5; 222 | h = bswap_32(h); 223 | f += a0; 224 | PERMUTE3(f, h, g); 225 | s += 20; 226 | } while (--iters != 0); 227 | g = Rotate32(g, 11) * c1; 228 | g = Rotate32(g, 17) * c1; 229 | f = Rotate32(f, 11) * c1; 230 | f = Rotate32(f, 17) * c1; 231 | h = Rotate32(h + g, 19); 232 | h = h * 5 + 0xe6546b64; 233 | h = Rotate32(h, 17) * c1; 234 | h = Rotate32(h + f, 19); 235 | h = h * 5 + 0xe6546b64; 236 | h = Rotate32(h, 17) * c1; 237 | return h; 238 | } 239 | 240 | // Bitwise right rotate. Normally this will compile to a single 241 | // instruction, especially if the shift is a manifest constant. 242 | static uint64 Rotate(uint64 val, int shift) { 243 | // Avoid shifting by 64: doing so yields an undefined result. 244 | return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); 245 | } 246 | 247 | static uint64 ShiftMix(uint64 val) { 248 | return val ^ (val >> 47); 249 | } 250 | 251 | static uint64 HashLen16(uint64 u, uint64 v) { 252 | return Hash128to64(uint128(u, v)); 253 | } 254 | 255 | static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { 256 | // Murmur-inspired hashing. 257 | uint64 a = (u ^ v) * mul; 258 | a ^= (a >> 47); 259 | uint64 b = (v ^ a) * mul; 260 | b ^= (b >> 47); 261 | b *= mul; 262 | return b; 263 | } 264 | 265 | static uint64 HashLen0to16(const char *s, size_t len) { 266 | if (len >= 8) { 267 | uint64 mul = k2 + len * 2; 268 | uint64 a = Fetch64(s) + k2; 269 | uint64 b = Fetch64(s + len - 8); 270 | uint64 c = Rotate(b, 37) * mul + a; 271 | uint64 d = (Rotate(a, 25) + b) * mul; 272 | return HashLen16(c, d, mul); 273 | } 274 | if (len >= 4) { 275 | uint64 mul = k2 + len * 2; 276 | uint64 a = Fetch32(s); 277 | return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); 278 | } 279 | if (len > 0) { 280 | uint8 a = s[0]; 281 | uint8 b = s[len >> 1]; 282 | uint8 c = s[len - 1]; 283 | uint32 y = static_cast(a) + (static_cast(b) << 8); 284 | uint32 z = len + (static_cast(c) << 2); 285 | return ShiftMix(y * k2 ^ z * k0) * k2; 286 | } 287 | return k2; 288 | } 289 | 290 | // This probably works well for 16-byte strings as well, but it may be overkill 291 | // in that case. 292 | static uint64 HashLen17to32(const char *s, size_t len) { 293 | uint64 mul = k2 + len * 2; 294 | uint64 a = Fetch64(s) * k1; 295 | uint64 b = Fetch64(s + 8); 296 | uint64 c = Fetch64(s + len - 8) * mul; 297 | uint64 d = Fetch64(s + len - 16) * k2; 298 | return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, 299 | a + Rotate(b + k2, 18) + c, mul); 300 | } 301 | 302 | // Return a 16-byte hash for 48 bytes. Quick and dirty. 303 | // Callers do best to use "random-looking" values for a and b. 304 | static pair WeakHashLen32WithSeeds( 305 | uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) { 306 | a += w; 307 | b = Rotate(b + a + z, 21); 308 | uint64 c = a; 309 | a += x; 310 | a += y; 311 | b += Rotate(a, 44); 312 | return make_pair(a + z, b + c); 313 | } 314 | 315 | // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. 316 | static pair WeakHashLen32WithSeeds( 317 | const char* s, uint64 a, uint64 b) { 318 | return WeakHashLen32WithSeeds(Fetch64(s), 319 | Fetch64(s + 8), 320 | Fetch64(s + 16), 321 | Fetch64(s + 24), 322 | a, 323 | b); 324 | } 325 | 326 | // Return an 8-byte hash for 33 to 64 bytes. 327 | static uint64 HashLen33to64(const char *s, size_t len) { 328 | uint64 mul = k2 + len * 2; 329 | uint64 a = Fetch64(s) * k2; 330 | uint64 b = Fetch64(s + 8); 331 | uint64 c = Fetch64(s + len - 24); 332 | uint64 d = Fetch64(s + len - 32); 333 | uint64 e = Fetch64(s + 16) * k2; 334 | uint64 f = Fetch64(s + 24) * 9; 335 | uint64 g = Fetch64(s + len - 8); 336 | uint64 h = Fetch64(s + len - 16) * mul; 337 | uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9; 338 | uint64 v = ((a + g) ^ d) + f + 1; 339 | uint64 w = bswap_64((u + v) * mul) + h; 340 | uint64 x = Rotate(e + f, 42) + c; 341 | uint64 y = (bswap_64((v + w) * mul) + g) * mul; 342 | uint64 z = e + f + c; 343 | a = bswap_64((x + z) * mul + y) + b; 344 | b = ShiftMix((z + a) * mul + d + h) * mul; 345 | return b + x; 346 | } 347 | 348 | uint64 CityHash64(const char *s, size_t len) { 349 | if (len <= 32) { 350 | if (len <= 16) { 351 | return HashLen0to16(s, len); 352 | } else { 353 | return HashLen17to32(s, len); 354 | } 355 | } else if (len <= 64) { 356 | return HashLen33to64(s, len); 357 | } 358 | 359 | // For strings over 64 bytes we hash the end first, and then as we 360 | // loop we keep 56 bytes of state: v, w, x, y, and z. 361 | uint64 x = Fetch64(s + len - 40); 362 | uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); 363 | uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); 364 | pair v = WeakHashLen32WithSeeds(s + len - 64, len, z); 365 | pair w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); 366 | x = x * k1 + Fetch64(s); 367 | 368 | // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. 369 | len = (len - 1) & ~static_cast(63); 370 | do { 371 | x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; 372 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 373 | x ^= w.second; 374 | y += v.first + Fetch64(s + 40); 375 | z = Rotate(z + w.first, 33) * k1; 376 | v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); 377 | w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); 378 | std::swap(z, x); 379 | s += 64; 380 | len -= 64; 381 | } while (len != 0); 382 | return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, 383 | HashLen16(v.second, w.second) + x); 384 | } 385 | 386 | uint64 CityHash64WithSeed(const char *s, size_t len, uint64 seed) { 387 | return CityHash64WithSeeds(s, len, k2, seed); 388 | } 389 | 390 | uint64 CityHash64WithSeeds(const char *s, size_t len, 391 | uint64 seed0, uint64 seed1) { 392 | return HashLen16(CityHash64(s, len) - seed0, seed1); 393 | } 394 | 395 | // A subroutine for CityHash128(). Returns a decent 128-bit hash for strings 396 | // of any length representable in signed long. Based on City and Murmur. 397 | static uint128 CityMurmur(const char *s, size_t len, uint128 seed) { 398 | uint64 a = Uint128Low64(seed); 399 | uint64 b = Uint128High64(seed); 400 | uint64 c = 0; 401 | uint64 d = 0; 402 | signed long l = len - 16; 403 | if (l <= 0) { // len <= 16 404 | a = ShiftMix(a * k1) * k1; 405 | c = b * k1 + HashLen0to16(s, len); 406 | d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); 407 | } else { // len > 16 408 | c = HashLen16(Fetch64(s + len - 8) + k1, a); 409 | d = HashLen16(b + len, c + Fetch64(s + len - 16)); 410 | a += d; 411 | do { 412 | a ^= ShiftMix(Fetch64(s) * k1) * k1; 413 | a *= k1; 414 | b ^= a; 415 | c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; 416 | c *= k1; 417 | d ^= c; 418 | s += 16; 419 | l -= 16; 420 | } while (l > 0); 421 | } 422 | a = HashLen16(a, c); 423 | b = HashLen16(d, b); 424 | return uint128(a ^ b, HashLen16(b, a)); 425 | } 426 | 427 | uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) { 428 | if (len < 128) { 429 | return CityMurmur(s, len, seed); 430 | } 431 | 432 | // We expect len >= 128 to be the common case. Keep 56 bytes of state: 433 | // v, w, x, y, and z. 434 | pair v, w; 435 | uint64 x = Uint128Low64(seed); 436 | uint64 y = Uint128High64(seed); 437 | uint64 z = len * k1; 438 | v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); 439 | v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); 440 | w.first = Rotate(y + z, 35) * k1 + x; 441 | w.second = Rotate(x + Fetch64(s + 88), 53) * k1; 442 | 443 | // This is the same inner loop as CityHash64(), manually unrolled. 444 | do { 445 | x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; 446 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 447 | x ^= w.second; 448 | y += v.first + Fetch64(s + 40); 449 | z = Rotate(z + w.first, 33) * k1; 450 | v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); 451 | w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); 452 | std::swap(z, x); 453 | s += 64; 454 | x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; 455 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 456 | x ^= w.second; 457 | y += v.first + Fetch64(s + 40); 458 | z = Rotate(z + w.first, 33) * k1; 459 | v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); 460 | w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); 461 | std::swap(z, x); 462 | s += 64; 463 | len -= 128; 464 | } while (LIKELY(len >= 128)); 465 | x += Rotate(v.first + z, 49) * k0; 466 | y = y * k0 + Rotate(w.second, 37); 467 | z = z * k0 + Rotate(w.first, 27); 468 | w.first *= 9; 469 | v.first *= k0; 470 | // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. 471 | for (size_t tail_done = 0; tail_done < len; ) { 472 | tail_done += 32; 473 | y = Rotate(x + y, 42) * k0 + v.second; 474 | w.first += Fetch64(s + len - tail_done + 16); 475 | x = x * k0 + w.first; 476 | z += w.second + Fetch64(s + len - tail_done); 477 | w.second += v.first; 478 | v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second); 479 | v.first *= k0; 480 | } 481 | // At this point our 56 bytes of state should contain more than 482 | // enough information for a strong 128-bit hash. We use two 483 | // different 56-byte-to-8-byte hashes to get a 16-byte final result. 484 | x = HashLen16(x, v.first); 485 | y = HashLen16(y + z, w.first); 486 | return uint128(HashLen16(x + v.second, w.second) + y, 487 | HashLen16(x + w.second, y + v.second)); 488 | } 489 | 490 | uint128 CityHash128(const char *s, size_t len) { 491 | return len >= 16 ? 492 | CityHash128WithSeed(s + 16, len - 16, 493 | uint128(Fetch64(s), Fetch64(s + 8) + k0)) : 494 | CityHash128WithSeed(s, len, uint128(k0, k1)); 495 | } 496 | 497 | #ifdef __SSE4_2__ 498 | #include 499 | #include 500 | 501 | // Requires len >= 240. 502 | static void CityHashCrc256Long(const char *s, size_t len, 503 | uint32 seed, uint64 *result) { 504 | uint64 a = Fetch64(s + 56) + k0; 505 | uint64 b = Fetch64(s + 96) + k0; 506 | uint64 c = result[0] = HashLen16(b, len); 507 | uint64 d = result[1] = Fetch64(s + 120) * k0 + len; 508 | uint64 e = Fetch64(s + 184) + seed; 509 | uint64 f = 0; 510 | uint64 g = 0; 511 | uint64 h = c + d; 512 | uint64 x = seed; 513 | uint64 y = 0; 514 | uint64 z = 0; 515 | 516 | // 240 bytes of input per iter. 517 | size_t iters = len / 240; 518 | len -= iters * 240; 519 | do { 520 | #undef CHUNK 521 | #define CHUNK(r) \ 522 | PERMUTE3(x, z, y); \ 523 | b += Fetch64(s); \ 524 | c += Fetch64(s + 8); \ 525 | d += Fetch64(s + 16); \ 526 | e += Fetch64(s + 24); \ 527 | f += Fetch64(s + 32); \ 528 | a += b; \ 529 | h += f; \ 530 | b += c; \ 531 | f += d; \ 532 | g += e; \ 533 | e += z; \ 534 | g += x; \ 535 | z = _mm_crc32_u64(z, b + g); \ 536 | y = _mm_crc32_u64(y, e + h); \ 537 | x = _mm_crc32_u64(x, f + a); \ 538 | e = Rotate(e, r); \ 539 | c += e; \ 540 | s += 40 541 | 542 | CHUNK(0); PERMUTE3(a, h, c); 543 | CHUNK(33); PERMUTE3(a, h, f); 544 | CHUNK(0); PERMUTE3(b, h, f); 545 | CHUNK(42); PERMUTE3(b, h, d); 546 | CHUNK(0); PERMUTE3(b, h, e); 547 | CHUNK(33); PERMUTE3(a, h, e); 548 | } while (--iters > 0); 549 | 550 | while (len >= 40) { 551 | CHUNK(29); 552 | e ^= Rotate(a, 20); 553 | h += Rotate(b, 30); 554 | g ^= Rotate(c, 40); 555 | f += Rotate(d, 34); 556 | PERMUTE3(c, h, g); 557 | len -= 40; 558 | } 559 | if (len > 0) { 560 | s = s + len - 40; 561 | CHUNK(33); 562 | e ^= Rotate(a, 43); 563 | h += Rotate(b, 42); 564 | g ^= Rotate(c, 41); 565 | f += Rotate(d, 40); 566 | } 567 | result[0] ^= h; 568 | result[1] ^= g; 569 | g += h; 570 | a = HashLen16(a, g + z); 571 | x += y << 32; 572 | b += x; 573 | c = HashLen16(c, z) + h; 574 | d = HashLen16(d, e + result[0]); 575 | g += e; 576 | h += HashLen16(x, f); 577 | e = HashLen16(a, d) + g; 578 | z = HashLen16(b, c) + a; 579 | y = HashLen16(g, h) + c; 580 | result[0] = e + z + y + x; 581 | a = ShiftMix((a + y) * k0) * k0 + b; 582 | result[1] += a + result[0]; 583 | a = ShiftMix(a * k0) * k0 + c; 584 | result[2] = a + result[1]; 585 | a = ShiftMix((a + e) * k0) * k0; 586 | result[3] = a + result[2]; 587 | } 588 | 589 | // Requires len < 240. 590 | static void CityHashCrc256Short(const char *s, size_t len, uint64 *result) { 591 | char buf[240]; 592 | memcpy(buf, s, len); 593 | memset(buf + len, 0, 240 - len); 594 | CityHashCrc256Long(buf, 240, ~static_cast(len), result); 595 | } 596 | 597 | void CityHashCrc256(const char *s, size_t len, uint64 *result) { 598 | if (LIKELY(len >= 240)) { 599 | CityHashCrc256Long(s, len, 0, result); 600 | } else { 601 | CityHashCrc256Short(s, len, result); 602 | } 603 | } 604 | 605 | uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) { 606 | if (len <= 900) { 607 | return CityHash128WithSeed(s, len, seed); 608 | } else { 609 | uint64 result[4]; 610 | CityHashCrc256(s, len, result); 611 | uint64 u = Uint128High64(seed) + result[0]; 612 | uint64 v = Uint128Low64(seed) + result[1]; 613 | return uint128(HashLen16(u, v + result[2]), 614 | HashLen16(Rotate(v, 32), u * k0 + result[3])); 615 | } 616 | } 617 | 618 | uint128 CityHashCrc128(const char *s, size_t len) { 619 | if (len <= 900) { 620 | return CityHash128(s, len); 621 | } else { 622 | uint64 result[4]; 623 | CityHashCrc256(s, len, result); 624 | return uint128(result[2], result[3]); 625 | } 626 | } 627 | 628 | #endif 629 | -------------------------------------------------------------------------------- /src/third-party/cityhash/city.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Google, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | // 21 | // CityHash, by Geoff Pike and Jyrki Alakuijala 22 | // 23 | // http://code.google.com/p/cityhash/ 24 | // 25 | // This file provides a few functions for hashing strings. All of them are 26 | // high-quality functions in the sense that they pass standard tests such 27 | // as Austin Appleby's SMHasher. They are also fast. 28 | // 29 | // For 64-bit x86 code, on short strings, we don't know of anything faster than 30 | // CityHash64 that is of comparable quality. We believe our nearest competitor 31 | // is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash 32 | // tables and most other hashing (excluding cryptography). 33 | // 34 | // For 64-bit x86 code, on long strings, the picture is more complicated. 35 | // On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc., 36 | // CityHashCrc128 appears to be faster than all competitors of comparable 37 | // quality. CityHash128 is also good but not quite as fast. We believe our 38 | // nearest competitor is Bob Jenkins' Spooky. We don't have great data for 39 | // other 64-bit CPUs, but for long strings we know that Spooky is slightly 40 | // faster than CityHash on some relatively recent AMD x86-64 CPUs, for example. 41 | // Note that CityHashCrc128 is declared in citycrc.h. 42 | // 43 | // For 32-bit x86 code, we don't know of anything faster than CityHash32 that 44 | // is of comparable quality. We believe our nearest competitor is Murmur3A. 45 | // (On 64-bit CPUs, it is typically faster to use the other CityHash variants.) 46 | // 47 | // Functions in the CityHash family are not suitable for cryptography. 48 | // 49 | // Please see CityHash's README file for more details on our performance 50 | // measurements and so on. 51 | // 52 | // WARNING: This code has been only lightly tested on big-endian platforms! 53 | // It is known to work well on little-endian platforms that have a small penalty 54 | // for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. 55 | // It should work on all 32-bit and 64-bit platforms that allow unaligned reads; 56 | // bug reports are welcome. 57 | // 58 | // By the way, for some hash functions, given strings a and b, the hash 59 | // of a+b is easily derived from the hashes of a and b. This property 60 | // doesn't hold for any hash functions in this file. 61 | 62 | #ifndef CITY_HASH_H_ 63 | #define CITY_HASH_H_ 64 | 65 | #include // for size_t. 66 | #include 67 | #include 68 | 69 | typedef uint8_t uint8; 70 | typedef uint32_t uint32; 71 | typedef uint64_t uint64; 72 | typedef std::pair uint128; 73 | 74 | inline uint64 Uint128Low64(const uint128& x) { return x.first; } 75 | inline uint64 Uint128High64(const uint128& x) { return x.second; } 76 | 77 | // Hash function for a byte array. 78 | uint64 CityHash64(const char *buf, size_t len); 79 | 80 | // Hash function for a byte array. For convenience, a 64-bit seed is also 81 | // hashed into the result. 82 | uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed); 83 | 84 | // Hash function for a byte array. For convenience, two seeds are also 85 | // hashed into the result. 86 | uint64 CityHash64WithSeeds(const char *buf, size_t len, 87 | uint64 seed0, uint64 seed1); 88 | 89 | // Hash function for a byte array. 90 | uint128 CityHash128(const char *s, size_t len); 91 | 92 | // Hash function for a byte array. For convenience, a 128-bit seed is also 93 | // hashed into the result. 94 | uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed); 95 | 96 | // Hash function for a byte array. Most useful in 32-bit binaries. 97 | uint32 CityHash32(const char *buf, size_t len); 98 | 99 | // Hash 128 input bits down to 64 bits of output. 100 | // This is intended to be a reasonably good hash function. 101 | inline uint64 Hash128to64(const uint128& x) { 102 | // Murmur-inspired hashing. 103 | const uint64 kMul = 0x9ddfea08eb382d69ULL; 104 | uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; 105 | a ^= (a >> 47); 106 | uint64 b = (Uint128High64(x) ^ a) * kMul; 107 | b ^= (b >> 47); 108 | b *= kMul; 109 | return b; 110 | } 111 | 112 | #endif // CITY_HASH_H_ 113 | -------------------------------------------------------------------------------- /src/third-party/cityhash/citycrc.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Google, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | // 21 | // CityHash, by Geoff Pike and Jyrki Alakuijala 22 | // 23 | // This file declares the subset of the CityHash functions that require 24 | // _mm_crc32_u64(). See the CityHash README for details. 25 | // 26 | // Functions in the CityHash family are not suitable for cryptography. 27 | 28 | #ifndef CITY_HASH_CRC_H_ 29 | #define CITY_HASH_CRC_H_ 30 | 31 | #include 32 | 33 | // Hash function for a byte array. 34 | uint128 CityHashCrc128(const char *s, size_t len); 35 | 36 | // Hash function for a byte array. For convenience, a 128-bit seed is also 37 | // hashed into the result. 38 | uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed); 39 | 40 | // Hash function for a byte array. Sets result[0] ... result[3]. 41 | void CityHashCrc256(const char *s, size_t len, uint64 *result); 42 | 43 | #endif // CITY_HASH_CRC_H_ 44 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++ -*- 2 | 3 | #ifndef UTIL_H 4 | #define UTIL_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Compute the length of an integer (i.e. position of first 1 bit) 12 | // at compile-time. 13 | template 14 | struct integer_length { 15 | enum { value = 1 + integer_length::value, }; 16 | }; 17 | 18 | template<> 19 | struct integer_length<1> { 20 | enum { value = 1 }; 21 | }; 22 | 23 | // Returns a bitmask with the lowest n bits set to 1, all other bits 24 | // set to 0. 25 | constexpr uint64_t mask_n_bits(uint64_t n) { 26 | return (UINT64_C(1) << n) - 1; 27 | } 28 | 29 | // Streams: 30 | // 31 | // Streams are a lazily computed sequence of records of a given 32 | // type. Streams have the following interface: 33 | // 34 | // - empty() - Returns true if the sequence has been exhausted; 35 | // no operations other than calls to empty() or deallocation 36 | // should be done on an empty stream. 37 | // - next() - Fetches the next record. Returns true if a record 38 | // was fetched, false otherwise. In the latter case, the stream 39 | // will be considered empty (with all the previously stated 40 | // restrictions). 41 | // - value() - Returns a reference to the latest decoded record (may not be 42 | // called if no records have been read yet). Valid only until next 43 | // call to next(). 44 | 45 | // A Stream that treats the memory range "begin" to "end" as 46 | // an array of T. 47 | template 48 | struct PointerStream { 49 | PointerStream(const T* begin, const T* end) 50 | : begin_(begin - 1), end_(end) { 51 | } 52 | 53 | bool empty() const { return begin_ == end_; } 54 | bool next() { ++begin_; return !empty(); } 55 | const T& value() const { return *begin_; } 56 | 57 | private: 58 | const T* begin_; 59 | const T* end_; 60 | }; 61 | 62 | // A Stream that merges together multiple streams. value() 63 | // and next() will only operate on the stream whose head value 64 | // is the smallest. The unstated assumption is that the 65 | // component streams are sorted. 66 | // 67 | // If DeleteDuplicates is true, ensures there are no adjacent 68 | // duplicates in the output stream. That is: 69 | // 70 | // a = value(); next(); b = value(); 71 | // assert(a != b); 72 | // 73 | // This is internally implemented using a heap-based priority 74 | // queue. 75 | template 76 | struct SortedStreamInterleaver { 77 | SortedStreamInterleaver() { 78 | } 79 | 80 | ~SortedStreamInterleaver() { 81 | while (!streams_.empty()) { 82 | delete streams_.top(); 83 | streams_.pop(); 84 | } 85 | } 86 | 87 | // Registers "stream" as one of the component streams that 88 | // will be merged together. Takes ownership of the stream. 89 | void add_stream(Stream* stream) { 90 | if (stream->next()) { 91 | streams_.push(stream); 92 | empty_ = false; 93 | } else { 94 | delete stream; 95 | } 96 | } 97 | 98 | bool empty() { 99 | return empty_; 100 | } 101 | 102 | bool next() { 103 | if (streams_.empty()) { 104 | empty_ = true; 105 | return false; 106 | } 107 | 108 | // God, the priority_queue interface is hard to use. 109 | auto top_stream = streams_.top(); 110 | T value = top_stream->value(); 111 | streams_.pop(); 112 | if (top_stream->next()) { 113 | streams_.push(top_stream); 114 | } else { 115 | delete top_stream; 116 | } 117 | 118 | if (DeleteDuplicates) { 119 | if (value == top_) { 120 | return next(); 121 | } 122 | } 123 | 124 | top_ = value; 125 | return true; 126 | } 127 | 128 | const T& value() const { 129 | return top_; 130 | } 131 | 132 | private: 133 | T top_; 134 | bool empty_ = false; 135 | 136 | struct Cmp { 137 | bool operator()(const Stream* a, const Stream* b) { 138 | // Note: invert the comparison since priority_queue 139 | // is by default a max-heap rather than a min-heap. 140 | return *b < *a; 141 | } 142 | }; 143 | 144 | std::priority_queue, Cmp> streams_; 145 | }; 146 | 147 | // A stream that takes two input streams of type K and V, and produces 148 | // output records of type Pair. If the input streams are of 149 | // a different length, is exhausted when the first of the input 150 | // streams is exhausted. 151 | template 152 | struct StreamPairer { 153 | // Like std::pair, except comparisons only affect "first". 154 | class Pair { 155 | public: 156 | Pair() { 157 | } 158 | 159 | Pair(const K& key, V value) 160 | : first(key), second(value) { 161 | } 162 | 163 | bool operator<(const Pair& other) const { 164 | return first < other.first; 165 | } 166 | bool operator==(const Pair& other) const { 167 | return first == other.first; 168 | } 169 | 170 | K first; 171 | V second; 172 | }; 173 | 174 | // Does not take ownership of either of the input streams. 175 | StreamPairer(KeyStream* keys, ValueStream* values) 176 | : keys_(keys), 177 | values_(values) { 178 | } 179 | 180 | bool next() { 181 | bool kn = keys_->next(); 182 | bool vn = values_->next(); 183 | if (!kn || !vn) { 184 | empty_ = true; 185 | return false; 186 | } 187 | 188 | pair_.first = keys_->value(); 189 | pair_.second = values_->value(); 190 | return true; 191 | } 192 | 193 | bool empty() const { 194 | return empty_; 195 | } 196 | 197 | const Pair& value() const { 198 | return pair_; 199 | } 200 | 201 | bool operator<(const StreamPairer& other) const { 202 | return *keys_ < *other.keys_; 203 | } 204 | 205 | private: 206 | bool empty_ = false; 207 | Pair pair_; 208 | std::unique_ptr keys_; 209 | std::unique_ptr values_; 210 | }; 211 | 212 | #endif 213 | --------------------------------------------------------------------------------