#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <unordered_map>
#include <iomanip>
#include <regex>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <numeric>
using namespace std;
#define pii pair<long long , long long>
#define FAST ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL)
const long long dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
const long long MAX = 1000005;
const long long MOD = 1000000009;
int WIN_VALUE = 2048;
int board[4][4];
map<int, string> blank_space;
int length(int x){
string s = to_string(x);
return (int)s.size();
}
void print_board(int x, int y){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
if(i == x and j == y){
cout << board[i][j] << "*";
for(int k = 0; k < 4 - length(board[i][j]); k++){
cout << " ";
}
}
else{
cout << board[i][j];
for(int k = 0; k < 5 - length(board[i][j]); k++){
cout << " ";
}
}
}
cout << "\n";
}
}
void random_add(){
vector<int> empty_cells;
for(int i = 0; i < 16; i++){
if(board[i / 4 ][i % 4] == 0){
empty_cells.push_back(i);
}
}
if(empty_cells.size() == 0){
cout << "Game over! There are no more empty cells!\n";
return;
}
int r1 = empty_cells[rand() % (int)empty_cells.size()];
int r2 = rand() % 2 + 1;
board[r1 / 4][r1 % 4] = r2 * 2;
print_board(r1 / 4, r1 % 4);
empty_cells.clear();
}
vector<int> move_D(){
int ret[4][4] = {};
for(int i = 0; i < 4; i++){
vector<int> v;
for(int j = 3; j >= 0; j--){
if(board[i][j] == 0){
continue;
}
v.push_back(board[i][j]);
}
if(v.empty()){
continue;
}
else if(v.size() == 1){
ret[i][3] = v[0];
}
else if(v.size() == 2){
if(v[0] == v[1]){
ret[i][3] = v[0] * 2;
}
else{
ret[i][2] = v[1];
ret[i][3] = v[0];
}
}
else if(v.size() == 3){
if(v[0] == v[1]){
ret[i][2] = v[2];
ret[i][3] = v[0] * 2;
}
else if(v[1] == v[2]){
ret[i][2] = v[1] * 2;
ret[i][3] = v[0];
}
else{
for(int k = 0; k < 3; k++) ret[i][3 - k] = v[k];
}
}
else{
if(v[0] == v[1]){
if(v[2] == v[3]){
ret[i][2] = v[2] * 2;
ret[i][3] = v[0] * 2;
}
else{
ret[i][1] = v[3];
ret[i][2] = v[2];
ret[i][3] = v[0] * 2;
}
}
else if(v[1] == v[2]){
ret[i][1] = v[3];
ret[i][2] = v[1] * 2;
ret[i][3] = v[0];
}
else if(v[2] == v[3]){
ret[i][1] = v[2] * 2;
ret[i][2] = v[1];
ret[i][3] = v[0];
}
else{
for(int k = 0; k < 4; k++) ret[i][3 - k] = v[k];
}
}
}
vector<int> tmp;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
tmp.push_back(ret[i][j]);
}
}
return tmp;
}
vector<int> move_A(){
int ret[4][4] = {};
for(int i = 0; i < 4; i++){
vector<int> v;
for(int j = 0; j < 4; j++){
if(board[i][j] == 0){
continue;
}
v.push_back(board[i][j]);
}
if(v.empty()){
continue;
}
else if(v.size() == 1){
ret[i][0] = v[0];
}
else if(v.size() == 2){
if(v[0] == v[1]){
ret[i][0] = v[0] * 2;
}
else{
ret[i][1] = v[1];
ret[i][0] = v[0];
}
}
else if(v.size() == 3){
if(v[0] == v[1]){
ret[i][1] = v[2];
ret[i][0] = v[0] * 2;
}
else if(v[1] == v[2]){
ret[i][1] = v[1] * 2;
ret[i][0] = v[0];
}
else{
for(int k = 0; k < 3; k++) ret[i][k] = v[k];
}
}
else{
if(v[0] == v[1]){
if(v[2] == v[3]){
ret[i][1] = v[2] * 2;
ret[i][0] = v[0] * 2;
}
else{
ret[i][2] = v[3];
ret[i][1] = v[2];
ret[i][0] = v[0] * 2;
}
}
else if(v[1] == v[2]){
ret[i][2] = v[3];
ret[i][1] = v[1] * 2;
ret[i][0] = v[0];
}
else if(v[2] == v[3]){
ret[i][2] = v[2] * 2;
ret[i][1] = v[1];
ret[i][0] = v[0];
}
else{
for(int k = 0; k < 4; k++) ret[i][k] = v[k];
}
}
}
vector<int> tmp;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
tmp.push_back(ret[i][j]);
}
}
return tmp;
}
vector<int> move_W(){
int ret[4][4] = {};
for(int j = 0; j < 4; j++){
vector<int> v;
for(int i = 0; i < 4; i++){
if(board[i][j] == 0){
continue;
}
v.push_back(board[i][j]);
}
if(v.empty()){
continue;
}
else if(v.size() == 1){
ret[0][j] = v[0];
}
else if(v.size() == 2){
if(v[0] == v[1]){
ret[0][j] = v[0] * 2;
}
else{
ret[1][j] = v[1];
ret[0][j] = v[0];
}
}
else if(v.size() == 3){
if(v[0] == v[1]){
ret[1][j] = v[2];
ret[0][j] = v[0] * 2;
}
else if(v[1] == v[2]){
ret[1][j] = v[1] * 2;
ret[0][j] = v[0];
}
else{
for(int k = 0; k < 3; k++) ret[k][j] = v[k];
}
}
else{
if(v[0] == v[1]){
if(v[2] == v[3]){
ret[1][j] = v[2] * 2;
ret[0][j] = v[0] * 2;
}
else{
ret[2][j] = v[3];
ret[1][j] = v[2];
ret[0][j] = v[0] * 2;
}
}
else if(v[1] == v[2]){
ret[2][j] = v[3];
ret[1][j] = v[1] * 2;
ret[0][j] = v[0];
}
else if(v[2] == v[3]){
ret[2][j] = v[2] * 2;
ret[1][j] = v[1];
ret[0][j] = v[0];
}
else{
for(int k = 0; k < 4; k++) ret[k][j] = v[k];
}
}
}
vector<int> tmp;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
tmp.push_back(ret[i][j]);
}
}
return tmp;
}
vector<int> move_S(){
int ret[4][4] = {};
for(int j = 0; j < 4; j++){
vector<int> v;
for(int i = 3; i >= 0; i--){
if(board[i][j] == 0){
continue;
}
v.push_back(board[i][j]);
}
if(v.empty()){
continue;
}
else if(v.size() == 1){
ret[3][j] = v[0];
}
else if(v.size() == 2){
if(v[0] == v[1]){
ret[3][j] = v[0] * 2;
}
else{
ret[2][j] = v[1];
ret[3][j] = v[0];
}
}
else if(v.size() == 3){
if(v[0] == v[1]){
ret[2][j] = v[2];
ret[3][j] = v[0] * 2;
}
else if(v[1] == v[2]){
ret[2][j] = v[1] * 2;
ret[3][j] = v[0];
}
else{
for(int k = 0; k < 3; k++) ret[3 - k][j] = v[k];
}
}
else{
if(v[0] == v[1]){
if(v[2] == v[3]){
ret[2][j] = v[2] * 2;
ret[3][j] = v[0] * 2;
}
else{
ret[1][j] = v[3];
ret[2][j] = v[2];
ret[3][j] = v[0] * 2;
}
}
else if(v[1] == v[2]){
ret[1][j] = v[3];
ret[2][j] = v[1] * 2;
ret[3][j] = v[0];
}
else if(v[2] == v[3]){
ret[1][j] = v[2] * 2;
ret[2][j] = v[1];
ret[3][j] = v[0];
}
else{
for(int k = 0; k < 4; k++) ret[3 - k][j] = v[k];
}
}
}
vector<int> tmp;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
tmp.push_back(ret[i][j]);
}
}
return tmp;
}
bool check(vector<int> vec){
for(auto x : vec){
if(x == 0){
return true;
}
}
return false;
}
bool check2(){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
if(board[i][j] == 0){
return true;
}
}
}
return false;
}
bool check3(vector<int> vec){
vector<int> tmp;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
tmp.push_back(board[i][j]);
}
}
for(int i = 0; i < 16; i++){
if(vec[i] != tmp[i]){
return true;
}
}
return false;
}
void apply(vector<int> vec){
for(int i = 0; i < 16; i++){
board[i / 4][i % 4] = vec[i];
}
}
bool check_power(int x){
set<int> s;
int val = 8;
for(int i = 0; i < 11; i++){
s.insert(val);
val *= 2;
}
if(s.find(x) != s.end()){
return true;
}
else{
return false;
}
}
int main() {
//freopen("hayfeast.in", "r", stdin);
//freopen("hayfeast.out", "w", stdout);
FAST;
cout << "Welcome to 2048!\n\n";
cout << "Before you begin, enter a \"win value\" that is a positive power of 2 (between 8 and 8192) to reach!\nIf the board clogs up before you reach this value, you will lose the game, but if you successfully create this number, you win!\n\n";
cout << "Enter your win value: ";
while(true){
int x;
cin >> x;
cout << "\n";
if(!check_power(x)){
cout << "This number is not a positive power of 2 between 8 and 8192!\n\nRe-enter a new win value: ";
}
else{
WIN_VALUE = x;
cout << "Win value successfully set to " << x << "!\n\nType W, A, S, or D to move the tiles.\nTiles with the same number merge into one when they touch.\nAdd them up to reach 2048!\nA new number, randomly decided between 2 and 4, will be added to the board after each move.\nNewly added numbers have a \"*\" behind it.\nIf the board does not have an empty tile for a new number to be added, the board is considered to be clogged, and you lose the game!\n\nType \"EXIT\" to exit the game.\n\n";
break;
}
}
random_add();
while(true){
string c;
map<string, bool> m;
bool ck = check2();
bool pos = false;
map<string, vector<int>> m2;
m2["D"] = move_D();
m2["A"] = move_A();
m2["W"] = move_W();
m2["S"] = move_S();
if(!ck){
m["D"] = check(m2["D"]);
m["A"] = check(m2["A"]);
m["W"] = check(m2["W"]);
m["S"] = check(m2["S"]);
pos = m["D"] || m["A"] || m["W"] || m["S"];
if(!pos){
cout << "You lost!!!\n";
return 0;
}
}
cin >> c;
if(c == "EXIT"){
cout << "You have successfully exited 2048!\n";
break;
}
if(c != "D" and c!= "A" and c != "W" and c != "S"){
cout << "You have to type in W, A, S, or D in their capital forms!\n";
}
else{
if(!ck and pos and !m[c]){
cout << "You will lose if you swipe this way!\nChoose a different direction to swipe!\n";
}
else if(!check3(m2[c])){
cout << "This move doesn't do anything!\nChoose a different direction to swipe!\n";
}
else{
apply(m2[c]);
random_add();
}
}
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
if(board[i][j] == WIN_VALUE){
cout << "Congratulations!\nYou won!!!\n";
return 0;
}
}
}
}
}