pax_global_header00006660000000000000000000000064145144235300014513gustar00rootroot0000000000000052 comment=3eb237f7514c2533c2419f7107ab189ae2539dce 2048.c-1.0.0/000077500000000000000000000000001451442353000123275ustar00rootroot000000000000002048.c-1.0.0/.gitignore000066400000000000000000000001541451442353000143170ustar00rootroot00000000000000# Object files *.o # Libraries *.a # Shared objects *.so *.so.* # Executables 2048 # IDE .idea/ .vscode/2048.c-1.0.0/2048.c000066400000000000000000000263601451442353000130770ustar00rootroot00000000000000/* ============================================================================ Name : 2048.c Author : Maurits van der Schee Description : Console version of the game "2048" for GNU/Linux ============================================================================ */ #define _XOPEN_SOURCE 500 // for: usleep #include // defines: printf, puts, getchar #include // defines: EXIT_SUCCESS #include // defines: strcmp #include // defines: STDIN_FILENO, usleep #include // defines: termios, TCSANOW, ICANON, ECHO #include // defines: true, false #include // defines: uint8_t, uint32_t #include // defines: time #include // defines: signal, SIGINT #define SIZE 4 // this function receives 2 pointers (indicated by *) so it can set their values void getColors(uint8_t value, uint8_t scheme, uint8_t *foreground, uint8_t *background) { uint8_t original[] = {8, 255, 1, 255, 2, 255, 3, 255, 4, 255, 5, 255, 6, 255, 7, 255, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 255, 0, 255, 0}; uint8_t blackwhite[] = {232, 255, 234, 255, 236, 255, 238, 255, 240, 255, 242, 255, 244, 255, 246, 0, 248, 0, 249, 0, 250, 0, 251, 0, 252, 0, 253, 0, 254, 0, 255, 0}; uint8_t bluered[] = {235, 255, 63, 255, 57, 255, 93, 255, 129, 255, 165, 255, 201, 255, 200, 255, 199, 255, 198, 255, 197, 255, 196, 255, 196, 255, 196, 255, 196, 255, 196, 255}; uint8_t *schemes[] = {original, blackwhite, bluered}; // modify the 'pointed to' variables (using a * on the left hand of the assignment) *foreground = *(schemes[scheme] + (1 + value * 2) % sizeof(original)); *background = *(schemes[scheme] + (0 + value * 2) % sizeof(original)); // alternatively we could have returned a struct with two variables } uint8_t getDigitCount(uint32_t number) { uint8_t count = 0; do { number /= 10; count += 1; } while (number); return count; } void drawBoard(uint8_t board[SIZE][SIZE], uint8_t scheme, uint32_t score) { uint8_t x, y, fg, bg; printf("\033[H"); // move cursor to 0,0 printf("2048.c %17d pts\n\n", score); for (y = 0; y < SIZE; y++) { for (x = 0; x < SIZE; x++) { // send the addresses of the foreground and background variables, // so that they can be modified by the getColors function getColors(board[x][y], scheme, &fg, &bg); printf("\033[38;5;%d;48;5;%dm", fg, bg); // set color printf(" "); printf("\033[m"); // reset all modes } printf("\n"); for (x = 0; x < SIZE; x++) { getColors(board[x][y], scheme, &fg, &bg); printf("\033[38;5;%d;48;5;%dm", fg, bg); // set color if (board[x][y] != 0) { uint32_t number = 1 << board[x][y]; uint8_t t = 7 - getDigitCount(number); printf("%*s%u%*s", t - t / 2, "", number, t / 2, ""); } else { printf(" · "); } printf("\033[m"); // reset all modes } printf("\n"); for (x = 0; x < SIZE; x++) { getColors(board[x][y], scheme, &fg, &bg); printf("\033[38;5;%d;48;5;%dm", fg, bg); // set color printf(" "); printf("\033[m"); // reset all modes } printf("\n"); } printf("\n"); printf(" ←,↑,→,↓ or q \n"); printf("\033[A"); // one line up } uint8_t findTarget(uint8_t array[SIZE], uint8_t x, uint8_t stop) { uint8_t t; // if the position is already on the first, don't evaluate if (x == 0) { return x; } for (t = x - 1;; t--) { if (array[t] != 0) { if (array[t] != array[x]) { // merge is not possible, take next position return t + 1; } return t; } else { // we should not slide further, return this one if (t == stop) { return t; } } } // we did not find a target return x; } bool slideArray(uint8_t array[SIZE], uint32_t *score) { bool success = false; uint8_t x, t, stop = 0; for (x = 0; x < SIZE; x++) { if (array[x] != 0) { t = findTarget(array, x, stop); // if target is not original position, then move or merge if (t != x) { // if target is zero, this is a move if (array[t] == 0) { array[t] = array[x]; } else if (array[t] == array[x]) { // merge (increase power of two) array[t]++; // increase score *score += (uint32_t)1 << array[t]; // set stop to avoid double merge stop = t + 1; } array[x] = 0; success = true; } } } return success; } void rotateBoard(uint8_t board[SIZE][SIZE]) { uint8_t i, j, n = SIZE; uint8_t tmp; for (i = 0; i < n / 2; i++) { for (j = i; j < n - i - 1; j++) { tmp = board[i][j]; board[i][j] = board[j][n - i - 1]; board[j][n - i - 1] = board[n - i - 1][n - j - 1]; board[n - i - 1][n - j - 1] = board[n - j - 1][i]; board[n - j - 1][i] = tmp; } } } bool moveUp(uint8_t board[SIZE][SIZE], uint32_t *score) { bool success = false; uint8_t x; for (x = 0; x < SIZE; x++) { success |= slideArray(board[x], score); } return success; } bool moveLeft(uint8_t board[SIZE][SIZE], uint32_t *score) { bool success; rotateBoard(board); success = moveUp(board, score); rotateBoard(board); rotateBoard(board); rotateBoard(board); return success; } bool moveDown(uint8_t board[SIZE][SIZE], uint32_t *score) { bool success; rotateBoard(board); rotateBoard(board); success = moveUp(board, score); rotateBoard(board); rotateBoard(board); return success; } bool moveRight(uint8_t board[SIZE][SIZE], uint32_t *score) { bool success; rotateBoard(board); rotateBoard(board); rotateBoard(board); success = moveUp(board, score); rotateBoard(board); return success; } bool findPairDown(uint8_t board[SIZE][SIZE]) { bool success = false; uint8_t x, y; for (x = 0; x < SIZE; x++) { for (y = 0; y < SIZE - 1; y++) { if (board[x][y] == board[x][y + 1]) return true; } } return success; } uint8_t countEmpty(uint8_t board[SIZE][SIZE]) { uint8_t x, y; uint8_t count = 0; for (x = 0; x < SIZE; x++) { for (y = 0; y < SIZE; y++) { if (board[x][y] == 0) { count++; } } } return count; } bool gameEnded(uint8_t board[SIZE][SIZE]) { bool ended = true; if (countEmpty(board) > 0) return false; if (findPairDown(board)) return false; rotateBoard(board); if (findPairDown(board)) ended = false; rotateBoard(board); rotateBoard(board); rotateBoard(board); return ended; } void addRandom(uint8_t board[SIZE][SIZE]) { static bool initialized = false; uint8_t x, y; uint8_t r, len = 0; uint8_t n, list[SIZE * SIZE][2]; if (!initialized) { srand(time(NULL)); initialized = true; } for (x = 0; x < SIZE; x++) { for (y = 0; y < SIZE; y++) { if (board[x][y] == 0) { list[len][0] = x; list[len][1] = y; len++; } } } if (len > 0) { r = rand() % len; x = list[r][0]; y = list[r][1]; n = (rand() % 10) / 9 + 1; board[x][y] = n; } } void initBoard(uint8_t board[SIZE][SIZE]) { uint8_t x, y; for (x = 0; x < SIZE; x++) { for (y = 0; y < SIZE; y++) { board[x][y] = 0; } } addRandom(board); addRandom(board); } void setBufferedInput(bool enable) { static bool enabled = true; static struct termios old; struct termios new; if (enable && !enabled) { // restore the former settings tcsetattr(STDIN_FILENO, TCSANOW, &old); // set the new state enabled = true; } else if (!enable && enabled) { // get the terminal settings for standard input tcgetattr(STDIN_FILENO, &new); // we want to keep the old setting to restore them at the end old = new; // disable canonical mode (buffered i/o) and local echo new.c_lflag &= (~ICANON & ~ECHO); // set the new settings immediately tcsetattr(STDIN_FILENO, TCSANOW, &new); // set the new state enabled = false; } } int test() { uint8_t array[SIZE]; // these are exponents with base 2 (1=2 2=4 3=8) // data holds per line: 4x IN, 4x OUT, 1x POINTS uint8_t data[] = { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 4, 0, 1, 0, 1, 2, 0, 0, 0, 4, 1, 0, 0, 1, 2, 0, 0, 0, 4, 1, 0, 1, 0, 2, 0, 0, 0, 4, 1, 1, 1, 0, 2, 1, 0, 0, 4, 1, 0, 1, 1, 2, 1, 0, 0, 4, 1, 1, 0, 1, 2, 1, 0, 0, 4, 1, 1, 1, 1, 2, 2, 0, 0, 8, 2, 2, 1, 1, 3, 2, 0, 0, 12, 1, 1, 2, 2, 2, 3, 0, 0, 12, 3, 0, 1, 1, 3, 2, 0, 0, 4, 2, 0, 1, 1, 2, 2, 0, 0, 4}; uint8_t *in, *out, *points; uint8_t t, tests; uint8_t i; bool success = true; uint32_t score; tests = (sizeof(data) / sizeof(data[0])) / (2 * SIZE + 1); for (t = 0; t < tests; t++) { in = data + t * (2 * SIZE + 1); out = in + SIZE; points = in + 2 * SIZE; for (i = 0; i < SIZE; i++) { array[i] = in[i]; } score = 0; slideArray(array, &score); for (i = 0; i < SIZE; i++) { if (array[i] != out[i]) { success = false; } } if (score != *points) { success = false; } if (success == false) { for (i = 0; i < SIZE; i++) { printf("%d ", in[i]); } printf("=> "); for (i = 0; i < SIZE; i++) { printf("%d ", array[i]); } printf("(%d points) expected ", score); for (i = 0; i < SIZE; i++) { printf("%d ", in[i]); } printf("=> "); for (i = 0; i < SIZE; i++) { printf("%d ", out[i]); } printf("(%d points)\n", *points); break; } } if (success) { printf("All %u tests executed successfully\n", tests); } return !success; } void signal_callback_handler(int signum) { printf(" TERMINATED \n"); setBufferedInput(true); // make cursor visible, reset all modes printf("\033[?25h\033[m"); exit(signum); } int main(int argc, char *argv[]) { uint8_t board[SIZE][SIZE]; uint8_t scheme = 0; uint32_t score = 0; char c; bool success; if (argc == 2 && strcmp(argv[1], "test") == 0) { return test(); } if (argc == 2 && strcmp(argv[1], "blackwhite") == 0) { scheme = 1; } if (argc == 2 && strcmp(argv[1], "bluered") == 0) { scheme = 2; } // make cursor invisible, erase entire screen printf("\033[?25l\033[2J"); // register signal handler for when ctrl-c is pressed signal(SIGINT, signal_callback_handler); initBoard(board); setBufferedInput(false); drawBoard(board, scheme, score); while (true) { c = getchar(); if (c == -1) { puts("\nError! Cannot read keyboard input!"); break; } switch (c) { case 97: // 'a' key case 104: // 'h' key case 68: // left arrow success = moveLeft(board, &score); break; case 100: // 'd' key case 108: // 'l' key case 67: // right arrow success = moveRight(board, &score); break; case 119: // 'w' key case 107: // 'k' key case 65: // up arrow success = moveUp(board, &score); break; case 115: // 's' key case 106: // 'j' key case 66: // down arrow success = moveDown(board, &score); break; default: success = false; } if (success) { drawBoard(board, scheme, score); usleep(150 * 1000); // 150 ms addRandom(board); drawBoard(board, scheme, score); if (gameEnded(board)) { printf(" GAME OVER \n"); break; } } if (c == 'q') { printf(" QUIT? (y/n) \n"); c = getchar(); if (c == 'y') { break; } drawBoard(board, scheme, score); } if (c == 'r') { printf(" RESTART? (y/n) \n"); c = getchar(); if (c == 'y') { initBoard(board); score = 0; } drawBoard(board, scheme, score); } } setBufferedInput(true); // make cursor visible, reset all modes printf("\033[?25h\033[m"); return EXIT_SUCCESS; } 2048.c-1.0.0/2048.desktop000066400000000000000000000004671451442353000143260ustar00rootroot00000000000000[Desktop Entry] Name=2048 Comment=Add values sliding tiles until you reach 2048 Comment[es]=Alcanza el 2048 deslizando y sumando teselas Exec=sh -c '/usr/bin/2048;echo;echo PRESS ENTER TO EXIT;read line' Icon=2048 Terminal=true Type=Application Categories=Game;LogicGame; Keywords=2d;math;colour;single-player; 2048.c-1.0.0/LICENSE000066400000000000000000000020771451442353000133420ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Maurits van der Schee Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.2048.c-1.0.0/Makefile000066400000000000000000000003071451442353000137670ustar00rootroot00000000000000CFLAGS += -std=c99 PREFIX ?= /usr .PHONY: all clean test all: 2048 test: 2048 ./2048 test install: mkdir -p $(DESTDIR)$(PREFIX)/bin cp -p 2048 $(DESTDIR)$(PREFIX)/bin/2048 clean: rm -f 2048 2048.c-1.0.0/README.md000066400000000000000000000027271451442353000136160ustar00rootroot000000000000002048.c ====== [Spanish](README_es.md) ![screenshot](screenshot.png) Console version of the game "2048" for GNU/Linux ### Gameplay You can move the tiles in four directions using the arrow keys: up, down, left, and right. All numbers on the board will slide into that direction until they hit the wall and if they bump into each other then two numbers will be combined into one if they have the same value. Each number will only be combined once per move. Every move a new number 2 or 4 appears. If you have a 2048 on the board you have won, but you lose once the board is full and you cannot make a move. ### Requirements - C compiler Tested on: GNU/Linux, FreeBSD, OpenBSD ### Installation On Debian (based) systems: ``` sudo apt install 2048 ``` Or compile from source (recommended): ``` wget https://raw.githubusercontent.com/mevdschee/2048.c/master/2048.c gcc -o 2048 2048.c ./2048 ``` ### Running The game supports different color schemes. This depends on ANSI support for 88 or 256 colors. If there are not enough colors supported the game will fallback to black and white (still very much playable). For the original color scheme run: ``` ./2048 ``` For the black-to-white color scheme (requires 256 colors): ``` ./2048 blackwhite ``` For the blue-to-red color scheme (requires 256 colors): ``` ./2048 bluered ``` ### Contributing Contributions are very welcome. Always run the tests before committing using: ``` $ ./2048 test All 13 tests executed successfully ``` 2048.c-1.0.0/README_es.md000066400000000000000000000031761451442353000143040ustar00rootroot000000000000002048.c ====== [English](README.md) ![screenshot](screenshot.png) Version de consola del juego "2048" para GNU/Linux ### Cómo se juega Se pueden mover las teselas en las cuatro direcciones con las teclas de flecha: arriba, abajo, izquierda y derecha. Todos los números en el tablero se deslizarán en esa dirección hasta que choquen con la pared y, si se tocan entre sí, dos números se combinarán en uno si tienen el mismo valor. Cada número solo se combinará una vez por jugada. Cada movimiento aparece un nuevo número 2 o 4. Habrás ganado si consigues un 2048 en el tablero, pero perderás si el tablero está lleno y no puedes hacer más movimientos. ### Requisitos - Compilador de C Testeado en: GNU/Linux, FreeBSD, OpenBSD ### Instalación En sistemas Debian (basados): ``` sudo apto instalar 2048 ``` O compilar desde la fuente (recomendado): ``` wget https://raw.githubusercontent.com/mevdschee/2048.c/master/2048.c gcc -o 2048 2048.c ./2048 ``` ### Ejecutar el juego El juego soporta diferentes esquemas de colores. Esto depende del soporte ANSI para 88 o 256 colores. Si no hay los suficientes colores soportados, el juego recurrirá al blanco y negro (que sigue siendo apto para jugar). Para el esquema de colores original, ejecute en la consola: ``` ./2048 ``` Para el esquema de colores en blanco y negro (requiere 256 colores): ``` ./2048 blackwhite ``` Para el esquema de colores azul y rojo (requiere 256 colores): ``` ./2048 bluered ``` ### Contribuciones Las contribuciones son siempre bienvenidas. Ejecute siempre las pruebas antes de hacer commit usando: ``` $ ./2048 test All 13 tests executed successfully ``` 2048.c-1.0.0/debian_2048.png000066400000000000000000000054261451442353000147430ustar00rootroot00000000000000PNG  IHDR>abKGD pHYs  tIME 0 FiTXtCommentCreated with GIMPd.e zIDATx{pU={oH DDAV Q)jNNkR*8VR;i@(AJC=Zm;df~gowk=0ۜwձ_ő"@@@($e2`nuZ $p>@k;cYzc\!mI}Gq# 8,"KD!hKMP,$@!(K`d @)@@@>=FC@cY^4Iޒ*M::| 0m*/|JBS9qSt2k͛Y[CQ*?o( f\%[Xڧ6}`t<&`FwL.bn.ѺEԎʮ0c(FL }s@5f{^Bi3-"B]#qO*?Kc&fbnz_e᣶)<%Ӱ ݈ߡGCE- ?'"$0a}O4 .碊J:l`Ʉ*BpSɄw~;8n"*J٪).8y 'LvWm5g=H9哕#ʖwneaZ?mesב wJ7oXxU,N-sz(!', g'G@@@((<8бQTjiZRε]zU7-,Q^/GR&ہ/BgVw:Qc,4>Ej<4AmEMJhH5lϲe AF)22A " " E^KtPF B+4Pz.IPZ6j󱿷^>"p+/Ewsǜ(OiL{;v.TV+ðQCY_0r=fNbO.֍bܘm,zQHo__t73eOngRw0/\J.l ņUoPY[Gfş ~gͅVtzθƗآ|*N8Į-SVYݛwAAyG۳z.S?60nbaAZ-u1ԫ/6V¼_-`xośl]Qό΢]u5\AxvYɤzm>4w WG48.M guAU\|:$cQlr=V%ypd^e$<|WӍl<)JyG'nn 7ÉNzG:2{fMЬ.1{^=E*hm'*i}- [,03^tpdD*dF$'< ( 뻜JjF4:(ch΄DFJzzmOѓa>D#YVJQty~Һ3I%ckR*ڥ'H@@#   6APdec!"Vvvh ot"a'(R3`c0jư P˞^)iGѵ?x^<ǂ ɽY36EѳqTLj8{OY9X{(kZpvZiFdC=;r0 Oܩ+6unLQz܏SHK=y Hö_xa*n>\ͨ7251]_CWߗsr?5Hyٹf1Z }uVgh={֣ú矆Ha)F9P یnh;؆T P@@@>5$w͔PFtK4 -nyV^޵RH7$BkR7d3y7<4\\9ūЀoGw_Zemkȩ1ְ 7vN ~|:M:Gډߡf5lʥ ~ }\2rҘLĸON9B{#$vʲ=86iw6(ӆN*XN!_MzdX@@@@h3x(p<, R8r^l s}LQ6`tod    B}=IENDB`2048.c-1.0.0/palette.sh000066400000000000000000000020411451442353000143160ustar00rootroot00000000000000#!/bin/bash echo "[8 colors] => ^[[4#m" for color in {0..7} ; do #Display the color number=$(printf " %-5d " "$color") echo -en "\033[4${color}m${number}\033[0m" #Display 8 colors per line if [ $((($color + 1) % 8)) == 0 ] ; then echo #New line fi done echo "[16 colors] => ^[[48;5;#m" for color in {0..15} ; do #Display the color number=$(printf " %-5d " "$color") echo -en "\033[48;5;${color}m${number}\033[0m" #Display 8 colors per line if [ $((($color + 1) % 8)) == 0 ] ; then echo #New line fi done echo "[216 colors] => ^[[48;5;#m" for color in {16..231} ; do #Display the color number=$(printf " %-7d " "$color") echo -en "\033[48;5;${color}m${number}\033[0m" #Display 6 colors per line if [ $((($color - 15) % 6)) == 0 ] ; then echo #New line fi done echo "[24 shades of gray] => ^[[48;5;#m" for color in {232..255} ; do #Display the color number=$(printf " %-3d " "$color") echo -en "\033[48;5;${color}m${number}\033[0m" #Display 12 colors per line if [ $((($color - 231) % 12)) == 0 ] ; then echo #New line fi done 2048.c-1.0.0/screenshot.png000066400000000000000000000237261451442353000152240ustar00rootroot00000000000000PNG  IHDRX^OW sRGB pHYs  tIME,&~ IDATxiTW6[; .(7mb#DMo$l$f9Q:qQFuDD44Ww*tPp nn{{/%0 F  BQ^Xj 7:]X$0|{#]ҤR#EQE} Nf'`ױ4%)ЇG|l5Je}SyR 냀iu]cm\\,M?|d2?~B*ZeD,; 6Nk6|M^1F'%%tZlFA_y#d2ݿ}`PU)pv>HHqࡿQg^0˗߼yieM&h4 z^2 +Y7sNz TF.ɂsnbշCB"aCGjDcaӧOϛ7O(={adG,߽{`0t:UN,$|B1,}rxyE+.cY^^0Ms!^5 da'W w !۫߅\ EroRQNrA&γ"f3{ZVt:Nw]= Øfl0 ^7cj}U[32&~a׉z%#Cx JťFF .*=x MSM9H(8EBnČ  ?4pJ))*(킢HAIpgN$\++. SQ]f;Wf' %UWD9?7cD cmm1c更4e{p?t:`4z=.@>b|)eX"^Η -iH<|~鬉#nY-N_u2/M9/A1N&-_052dC/2c^d2Qopˏܸ[8V z=Rs!xʝ=\3K7 EY7-مU YPhJJDヿS.mh"IDF1xL_쥊fCྩ^3 ;a.)zS;Aޮ!UR,Frht>O<˳F~ꏑl; cNLWDoirٴ|.M4M.mzVKJJ:xgVz/7'OcIR$9\Vnw6rE~.!rc` .W}?=ǩcf?mkljtNB ow>.3Godh>T#u,e[ Ȼn6!xB:AzuB3a2iKkkQIa2;qKB$K?_c 9X(媞{[nS5!.ΥUU ~n !ֿhsyUMEUm?7@?_c+iHRe]<4<} ŘLu_%Fi1|4@x|i?2GS: PA ɯ#rR<׮]6lذa!;vܵ2bWWW-?VXaggg]H$x鮮oݺ0СC;v7Rw.:sh]R-,fgghZGGG??'C;T*4heKFF\.c#W6ޣR|}}gu:]w~8eF;[֥JLL~zgАf_~FiRYYY]Z;Nۼy={fs !;vcŋ}uD@ `9%M,VUU1 ٙBVVVXc̘1CZm2ZmSSϗɞQF9sӓے鸏˾;j^0T*%p_._ꫯr?_<|`T*Hd2*++===YD TWWrޞk}r2 S[[KQ{hgh4VWWƃѣG3m۶4-J .?m۶ Ku9RyL//y=^-ItHDҦxԅ\z5!ɓ|I~JJJo߾v.=!!;77~ʔ)'O.**JUUU .uVEEN|w[ZBB€_9s !{),,tuuU*2lҥ\u`_~eK p_nW^^%l6K,t{ΜUTTTQQ1uԘr۷ ={ >|ԩtzϞ=_rh߿p̙|>111/˲vܹnԨQ9~ƌ4M۷o~ģhjjR*rwS[CCCNNκu븑Bkkk}}}\.X(Zl6777sml bA9ry˲lnpl4V\T3 s̙>[;?? |ܯ8p`Ν{r?'Ѵl.]71c˗rdY2gquu9uw7'NpЃMMM7o~<,X`߾}|򉓓SKKe:74y5jܹ۶m~l3fge'O޻wO?{uFuر3g>Q*///KtA7UC?-'Mb2 Mn3}ZRm\{^vlӎ%_^_]]k׮˗/geeYT*:t+Wɩ4++˲e„ ۶m;p@ee%w9Y{{"C eee[tٳgƍ|MݵkWRRuطo7~>x:88p_zvo};@pCbccSRRzXܭ0c]0aRixLL̑#G!fȑ#7r>}zPPСCGTr^4/Sb BQSSh>3^׾l Z$''s+wcǖ.[{}/^XVVVRRRUUm?EPPFn )XnAyWnܸZ\\K/Y2xyy~7gΜٻw68Yxqee%hao{òlRRMbŋ婷~_~sNdD۷oߚ5k;q`Y@2 9dBB͛|UDDDdggg _zRjkkZO_v}ށ۷fNcǎiӦu₂W^yE(s<o޽ׯh4d.JRn9{ǏzxxD@nφ͘1cŊǧ-ԃoڷvR(m߾}|AEEŲe˺~t45رc۴kNN%޽qF=---FQTBz}XX^{~`iiioo`ĉ .z !QQQ]~Wxx]6##͍ewթBãbƌmy֬Ysl/ </))i֭ŋw),,!|ijk߇noo@(_|9sYELxGߖItb/fKljI?lRz:-7C/or%oTrg˶\/nh%,ݹdH 7N *ر!hf+e YСCrٌJEaFTRfA{d{xȥߌ_uZU_6kUu 3TXI9֥r!#""|}}q!ɨk)z8 h)fMmH°ͅUlxvvYrΟ??..zz5I;}|lfRl4P!Ѣ@Y`h5yNj޻W~ 6h4osc_ł  11bK.BP“\<2edoxb"%d.Zd'ZS6x!ySZ*n`u}Q9oqM7G嶙k:g.\HD|'7C/dn}=]MW]~g Z2]6fdFGZփ {2Qgw}W EB wA@  A@  AEr98@-0" A@  A@  +H/]jh= @B9|J5lH\O47N3Z6ChKTNPFf#q׮=0u/]4en2gkmqS:g)H\fE?>i>ND FSz&&߲po3f='255:[or{_ !>gǾ+ݹՌJESbFc:zt- igk$ZK괪&f3wKl<Nj޻W~ 6h4oTmgK1q)>-i6x) s"g---8ڽ죏DHD/^w\/ε @hG䔖۶rk:g.\HD|'7C/lT0bB1dbϟ߰!g˓v '6_wm\IPp32RѡwArrUrr4kVhd_k,8 dԙ;=(@t&ۺBڈX# A@  A@  O{= Ø3ws6}`(A  A@  A@}OoWq* :a-8:D:El4HՄ9y=*΢tChKTNPFTH GMq"O»{SSs1joݙ:!9;YY$p:1R23"}_}!~vX^jA؟ZJS<*UBJw^}Q1Z43ֳpU˦.*/.I;w^Q(>jQgllèF!eYBFpCaV^ӃdA*t^sv6RBX+|uҷqr`7`YE6ʂdu)u=[#Bׯ#Ӕp^0{nyyUZO!^ӽK%b5%EzF%l.ܜʧe78/'.z߾5kt558 {U6!Ϟ}59S<*zo*W>gcpOo( {hQ,LSf4Z 4w2Nj޻W~=N9/sCZ {˖)(ǫc|ҤK!-$c.LY.N6<12-קB0ggII?)k3f"/$/?*-\|fSW_1<Jɠ-z AFK*;tN:HQ[uWA@  A@  A]r90[[ z0F  A@  A@ tS_~K mȢ]!WiP(BHI1%AW7N~s l_.ҥISx&{F"MMu4igMJOE"LꙘ8|˖9sϘllFJwz5RQ|Ԣ"تÇQ#qBѩn5?}u!9;Q!,JT^.V3Zj}gt',[LӔT,(!4.%Ŗ/""Yo)'Ǹ߿`w zEۗfƖoqqqzK9Fct:ʺK_hƍ=[$Nj޻W~߆ 4RNA)-UR 1S603H ~~[ʉAٽ죏DHD/^w\}ϖ'|Z̅ i/$mT0bB1dbϟ߰!g3hJBt{ؘ\\eE5͚$0Y9HQ MM  A@  A@`v+*#@  A@  A@p''?P$uKҏ? 67/m/233Y=qD7e0聀3gfP(<|P("b#L&i4: [nذa]Ux BuVXm9sfRRʕ+꫎/++?LKKC>45inn9sS/]TT>4 ǵkrSN}Yf555}vvv"ͭ9~m!nB o?ƶyvժUy5ZmJJKJJ: ?~ԩS !:.>>~ݖ%Fe?#D神^xaС椤ٳgC_ XXXxj?|CCZ`nskk_R,VWWO(r%Rr9dz^]]>AhhhmmԦ_}UAAw{/c_΅6dYV*Z; ejE&),,{{aj}+!aaa/?֭[=:xŋ-),YVfssssk.\Xf !D(;vlƍO3gl޼"H炠Ǐ?pA_dN|||Ο?_]]]__d;}zO/Ur@ 7o{xx?~^T*UR$PuFA_wȑT 4Z5'  A@  A@  A@ )GIENDB`