«Скелет» игры Алхимия

19.11.2017

Существует множество вариаций этой игры, но как ее сделать самому — это совершенно другое, но на самом деле очень просто и не трудно.
Мой вариант это Android — самый простой способ написать мобильное приложение. Все писалось на скорую руку, без какой-либо подготовки, то есть все просто — захотел, сел написал. Поэтому некоторый код может быть выглядеть очень бредово и нелепо, но на то он и скелет — просто суть, без красоты 🙂

Структура базы

Данные любой игры необходимо хранить. Большой плюс платформы Android это возможность работы с базой SQLite — на ней и будет работать игра. Схема мне виделась такой — две таблицы, в одной список элементов, в другой список рецептов (упростим возможно задачу, но в рецепте только два элемента)
Схема БД

 

alchemyEngine

Теперь необходимо реализовать класс для работы с этой базой — самое первое объявим все необходимые процедуры

	
private static final int DB_VERSION = 1;
private static final String DB_NAME = Environment.getExternalStorageDirectory().getPath() +"/Alchemy/alchemy.db";

public alchemyEngine(Context context){
	super(context, DB_NAME, null, DB_VERSION);
}

Тут все понятно — версия БД и путь к БД.

Для примера работы в базе будут следующие элементы:
1. name1
2. name2
3. name3
4. name4
5. name5
6. name6
Причем name1 и name2 будут доступны сразу и это именно их надо смешивать, для получения других элементов.

Теперь рецепты — они будут такие:
1. name1 + name2 = name4
2. name1 + name4 = name3
3. name3 + name1 = name6 + name5

А процедура onCreate, в которой будет создаваться база будет иметь такой код:

	public void onCreate(SQLiteDatabase db) {

		String QUERY;
		QUERY = "CREATE TABLE elements(" +
				"_id INTEGER PRIMARY KEY," +
				"name TEXT NOT NULL," +
				"open INTEGER NOT NULL);";
		db.execSQL(QUERY);

		QUERY = "CREATE TABLE receptions(" +
				"_id INTEGER PRIMARY KEY," +
				"el1_id INTEGER NOT NULL," +
				"el2_id INTEGER NOT NULL," +
				"result_id INTEGER NOT NULL);";
		db.execSQL(QUERY);

		//// Elements
		// name1, name2, name3, name4, name5, name6

		QUERY = "INSERT INTO elements(name, open) VALUES('name1', 1);";
		db.execSQL(QUERY);
		QUERY = "INSERT INTO elements(name, open) VALUES('name2', 1);";
		db.execSQL(QUERY);
		QUERY = "INSERT INTO elements(name, open) VALUES('name3', 0);";
		db.execSQL(QUERY);
		QUERY = "INSERT INTO elements(name, open) VALUES('name4', 0);";
		db.execSQL(QUERY);
		QUERY = "INSERT INTO elements(name, open) VALUES('name5', 0);";
		db.execSQL(QUERY);		
		QUERY = "INSERT INTO elements(name, open) VALUES('name6', 0);";
		db.execSQL(QUERY); 		

		//// Receptions
 		// name1 + name2 = name4
		// name1 + name4 = name3
		// name3 + name1 = name6 + name5
		//		

		QUERY = "INSERT INTO receptions(el1_id, el2_id, result_id) VALUES(1, 2, 4);";
		db.execSQL(QUERY);
		QUERY = "INSERT INTO receptions(el1_id, el2_id, result_id) VALUES(1, 4, 3);";
		db.execSQL(QUERY);
		QUERY = "INSERT INTO receptions(el1_id, el2_id, result_id) VALUES(3, 1, 6);";
		db.execSQL(QUERY);
		QUERY = "INSERT INTO receptions(el1_id, el2_id, result_id) VALUES(3, 1, 5);";
		db.execSQL(QUERY);		

	}

Выбор элементов для смешивания будет реализован через Spinner (это же скелет). В него будут выводиться лишь строковые названия элементов, а смешивать будет по ID этих элементов — поэтому необходимо добавить две процедуры для поиска имени по ID и поиска ID по имени

	public int getIDbyName(String name){
		int id = 0;
		SQLiteDatabase db = this.getReadableDatabase();

		Cursor c = db.rawQuery("SELECT _id FROM elements WHERE name = '" + name.trim() + "'", null); 
		c.moveToNext();
		id = c.getInt(0);	

		return id;
	}

	public String getNameByID(int id){
		String name = "";
		SQLiteDatabase db = this.getReadableDatabase();

		Cursor c = db.rawQuery("SELECT name FROM elements WHERE _id = " + String.valueOf(id)+ ";", null); 
		c.moveToNext();

		name = c.getString(0);

		return name;
	}

Теперь осталось лишь реализовать процедуры для «открытия» элемента (проставление у стоблца open значения 1)

	public void openElement(int id){
		SQLiteDatabase db = this.getWritableDatabase();

		String QUERY = "UPDATE elements SET open = 1 WHERE _id = " + String.valueOf(id) + ";";
		db.execSQL(QUERY);
	}

И самая основная функция — соединение элементов. Функция будет возвращать список, а не просто ID элемента. Все потому что мы можешь «смешать» два элемента, а получить не один, а два или три (смотри Рецепт #3)

	public List<String> mixUp(int id1, int id2) {
		List<String> elementsList = new ArrayList<String>();

		String QUERY = "SELECT result_id FROM receptions WHERE (el1_id = " +String.valueOf(id1) + " AND el2_id = " + String.valueOf(id2) + ") OR (el1_id = " +String.valueOf(id2) + " AND el2_id = " + String.valueOf(id1) + ")";

		SQLiteDatabase db = this.getReadableDatabase();
		Cursor cursor = db.rawQuery(QUERY, null);

		if(cursor.moveToFirst()){
			do {
				elementsList.add(cursor.getString(0));
			}while(cursor.moveToNext());
		}

		return elementsList; 
	}

И само собой нужна функция для вывода списка доступных элементов, которая будет формировать список имен этих элементов

public List<String> elementesList() {
		List<String> elementsList = new ArrayList<String>();

		String SelectQuery = "SELECT _id, name FROM elements WHERE open = 1";

		SQLiteDatabase db = this.getReadableDatabase();
		Cursor cursor = db.rawQuery(SelectQuery, null);

		if(cursor.moveToFirst()){
			do {
				elementsList.add(cursor.getString(1));
			}while(cursor.moveToNext());
		}

		return elementsList;
	}

На это процедуры и функции класса alchemyEngine закончились.
Теперь необходимо описать Activity и все — игра готова 🙂

Activity
Как было сказано выше, на форме будет два Spinner и одна кнопка — в spinner будет выводиться имя элемента, а по нажатию на кнопку будет происходить «смешивание» элементов из обоих spinner. И лучше и проще через Toast вывести результаты этого смешивания.
Для каждого spinner напишем две процедуры для обновления данных в них:

	public void _updateSpinner1(){

		List<String> elementsList = new ArrayList<String>();
		elementsList = engine.elementesList();		

		Spinner my_spin = (Spinner)findViewById(R.id.spinner1);
		my_spin.setOnItemSelectedListener(this);
		ArrayAdapter<String> aa = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, elementsList);
		aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		my_spin.setAdapter(aa);		
	}

	public void _updateSpinner2(){

		List<String> elementsList = new ArrayList<String>();
		elementsList = engine.elementesList();		

		Spinner my_spin = (Spinner)findViewById(R.id.spinner2);
		my_spin.setOnItemSelectedListener(this);
		ArrayAdapter<String> aa = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, elementsList);
		aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		my_spin.setAdapter(aa);		
}

Для работы со spinner у нас одна процедура onItemSelected, но она должна обрабатывать сразу два spinner — я нашел такой способ для решения этой задачи:

	public void onItemSelected(AdapterView<?> parent, View arg1, int pos, long arg3) {

		Spinner spinner = (Spinner) parent;
	     if(spinner.getId() == R.id.spinner1)
	     {
	    	 element1 = spinner.getSelectedItem().toString();                 
	     }
	     else if(spinner.getId() == R.id.spinner2)
	     {
	    	 element2 = spinner.getSelectedItem().toString();
	     }
	}

И осталась сама я важная процедура — «смешивание» элементов.
Суть простая — берем имена из spinner перегоняем их в ID этих элементов, вызываем процедуру mixUp, в которую передаем два ID элементов и в ответ получаем список новых полученных элементов…или ничего. Код такой:

	public void mixElements(View v){
		int id1 = engine.getIDbyName(element1);
		int id2 = engine.getIDbyName(element2);
		String out = "";
		
		mixEl = new ArrayList<String>();
		mixEl = engine.mixUp(id1, id2);	
		
		if(mixEl.isEmpty()){
			Toast toast = Toast.makeText(getApplicationContext(), "Ничего не получилось!", Toast.LENGTH_SHORT); 
			toast.show();			
		} else {			
			for (String temp : mixEl) {
				int el_id = Integer.parseInt(temp);
				engine.openElement(el_id);
				String elName = engine.getNameByID(el_id);
				out = out + elName + " ";
			}
			
			Toast toast = Toast.makeText(getApplicationContext(), "Получились элементы: " + out, Toast.LENGTH_SHORT); 
			toast.show();			

			_updateSpinner1();
			_updateSpinner2();
			
		}		
	}

На этом получается вроде все. Основные моменты описаны, как и сама идея. Скачать архив с исходниками можно тут.

Всем успехов 😉

Tittygram