TlačiťTlačiť Slovenčina English Hľadať RSS

© 2005 – 2024 Roman Horváth, všetky práva vyhradené. Dnes je 19. 4. 2024.

Stránka sa načítava, prosím čakajte…

Dátum: 13. 12. 2020, pred štyrmi rokmi

Nim je klasická hra, v ktorej obvykle dvaja hráči striedavo odoberajú objekty (zápalky) z kôpok a ten hráč, ktorý odoberie posledný objekt (zápalku) buď prehrá, alebo vyhrá podľa toho, či ide o „normálnu“ hru, alebo tzv. misère. Aj keď je pri hre Nim bežnejší druhý variant, tu implementujeme „normálnu“ hru (lebo je jednoduchšia na implementáciu).

Tento variant je zjednodušený. Hráči hrajú s jedinou kôpkou zápaliek a môžu odoberať najviac dve zápalky naraz. Vychádzajúc z ukážky SVG kreslenie sme kreslenie implementovali s pomocou SVG podpory rámca GRobot, ale externý SVG súbor sme nahradili klasickým reťazcom, ktorý obsahuje rovnakú SVG definíciu, aká by bola uložená v SVG súbore. Celá hra je tak uložená v jedinom zdrojovom kóde.

  
obrázok 
Ukážka hrania hry. 
 

Poznámka

V komentári zdrojového kódu triedy Nim (nižšie; v blízkosti metódy spracujSVG) je uložený jednoduchší variant SVG zápalky prepísaný do reťazca Javy. Pod komentárom je zároveň uvedený mierne zložitejší variant.

Originál jednoduchšej SVG definície je zverejnený v článku SVG kreslenie.

Originál zložitejšej SVG definície vyzerá takto:

<svg width="12" height="65" version="1.1" viewBox="0 0 12 65"
	xmlns="http://www.w3.org/2000/svg">
	<rect x="6.5" y="13" width="1.5" height="50" fill="#aa0"
		stroke="#000" stroke-width=".5"/>
	<rect x="4" y="13" width="2.5" height="50" fill="#cc0"
		stroke="#000" stroke-width=".5"/>
	<ellipse cx="6" cy="8" rx="4" ry="6" fill="#800"
		stroke="#000" stroke-width=".5"/>
	<ellipse cx="5.5" cy="7.5" rx="3" ry="5" fill="#a00"/>
	<ellipse cx="4.375" cy="4.625" rx=".875" ry=".875"
		fill="#fff" fill-opacity=".5"/>
</svg>

 

Zdrojový kód

~

import java.awt.geom.AffineTransform;
import knižnica.*;

import static knižnica.Svet.*;
import static java.io.File.separator;

public class Nim extends GRobot
{
	// Atribúty (a vymenovanie stavov).

	private enum Stav {NOVÁ_HRA, HRÁ_SA, VÝHRA, PREHRA}

	private int zápaliek;
	private Stav stav;


	// Konštrukcia.

	private Nim()
	{
		farbaPozadia(antracitová);
		farbaTextu(papierová);
		Svet.písmo("Cambria", 36);

		začniVstup();
		neskrývajVstupnýRiadok();

		nováHra();
		skry();
		mierka(3);
	}


	// Hra.

	private void nováHra()
	{
		vymažTexty();
		popisVstupnéhoRiadka("Zadajte počet zapáliek: ");
		zápaliek = 0;
		stav = Stav.NOVÁ_HRA;
		prekresliZápalky();
	}

	private void hráSa()
	{
		popisVstupnéhoRiadka("Koľko zápaliek chcete odobrať: ");
		bilancia();
		stav = Stav.HRÁ_SA;
	}

	private void výhra()
	{
		vypíšRiadok("Vyhrali ste!");
		popisVstupnéhoRiadka("Chcete začať novú hru? ");
		stav = Stav.VÝHRA;
		prekresliZápalky();
	}

	private void prehra()
	{
		vypíšRiadok("Prehrali ste…");
		popisVstupnéhoRiadka("Chcete začať novú hru? ");
		stav = Stav.PREHRA;
		prekresliZápalky();
	}

	private void spracujPočet()
	{
		Long číslo = prevezmiCeléČíslo();
		if (null == číslo)
		{
			pípni();
			textVstupnéhoRiadka(prevezmiReťazec());
		}
		else
		{
			int počet = číslo.intValue();
			if (počet < 5)
			{
				pípni();
				chyba("Minimálnmy počet je 5");
			}
			else
			{
				zápaliek = počet;
				hráSa();
			}
		}
	}

	private void hranieHry()
	{
		Long počet = prevezmiCeléČíslo();
		if (null == počet)
		{
			pípni();
			textVstupnéhoRiadka(prevezmiReťazec());
		}
		else if (počet < 1)
		{
			textVstupnéhoRiadka(prevezmiReťazec());
			pípni();
			chyba("Počet musí byť kladný.");
		}
		else if (počet > 2)
		{
			// textVstupnéhoRiadka(prevezmiReťazec());
			pípni();
			chyba("Môžete odobrať len jednu alebo dve zápalky.");
		}
		else
		{
			int odobrať = počet.intValue();
			if (odobrať > zápaliek) odobrať = zápaliek;
			if (1 == odobrať)
				vypíšRiadok("Odobrali ste jednu zápalku.");
			else
				vypíšRiadok("Odobrali ste dve zápalky.");
			zápaliek -= odobrať;
			if (zápaliek <= 0) výhra(); else
			{
				bilancia();
				akciaPočítača();
			}
		}
	}

	private void čakanie()
	{
		String odpoveď = prevezmiReťazec().trim().toLowerCase();
		if ("a".equals(odpoveď) || "á".equals(odpoveď) ||
			"ano".equals(odpoveď) || "áno".equals(odpoveď)) nováHra();
		else if ("n".equals(odpoveď) || "nie".equals(odpoveď)) koniec();
	}

	private void akciaPočítača()
	{
		int odobrať = (int)náhodnéCeléČíslo(1, 2);
		if (odobrať > zápaliek) odobrať = zápaliek;
		if (1 == odobrať)
			vypíšRiadok("Počítač odobral jednu zápalku.");
		else
			vypíšRiadok("Počítač odobral dve zápalky.");
		zápaliek -= odobrať;
		if (zápaliek <= 0) prehra(); else bilancia();
	}


	// Kreslenie.

	private void nakresliZápalku()
	{
		Farba farba = farba();
		double hrúbkaČiary = hrúbkaČiary();
		int počet = svgPodpora.počet();

		for (int i = 0; i < počet; ++i)
		{
			Farba farbaTvaru = svgPodpora.farbaVýplne(i);
			if (null != farbaTvaru)
			{
				farba(farbaTvaru);
				vyplňTvar(svgPodpora.dajVýsledný(i), true);
			}

			farbaTvaru = svgPodpora.farbaČiary(i);
			if (null != farbaTvaru)
			{
				double hrúbkaČiaryTvaru = svgPodpora.hrúbkaČiary(i) * mierka();
				if (Double.isFinite(hrúbkaČiaryTvaru))
					hrúbkaČiary(hrúbkaČiaryTvaru);
				else
					hrúbkaČiary(1);
				farba(farbaTvaru);
				kresliTvar(svgPodpora.dajVýsledný(i), true);
			}
		}

		hrúbkaČiary(hrúbkaČiary);
		farba(farba);
	}

	private void prekresliZápalky()
	{
		nekresli();
		podlaha.vymažGrafiku();
		Poloha p = poloha();
		preskočVľavo((zápaliek - 1) * 6 * mierka());

		for (int i = 0; i < zápaliek; ++i)
		{
			nakresliZápalku();
			preskočVpravo(12 * mierka());
		}

		skočNa(p);
		kresli();
	}

	private void bilancia()
	{
		vypíš("Na ploche ");
		switch (stav)
		{
		case NOVÁ_HRA:
			switch (zápaliek)
			{
			case 1: vypíš("je jedna zapálka"); break;
			case 2: case 3: case 4: vypíš("sú ", zápaliek, " zapálky"); break;
			default: vypíš("je ", zápaliek," zapáliek");
			}
			break;

		default:
			switch (zápaliek)
			{
			case 1: vypíš("zostala jedna zapálka"); break;
			case 2: case 3: case 4: vypíš("zostali ", zápaliek,
					" zapálky"); break;
			default: vypíš("zostalo ", zápaliek," zapáliek");
			}
			break;
		}

		vypíšRiadok(".");

		prekresliZápalky();
	}


	// Reakcie.

	@Override public void potvrdenieVstupu()
	{
		switch (stav)
		{
		case NOVÁ_HRA: spracujPočet(); break;
		case HRÁ_SA: hranieHry(); break;
		default: čakanie();
		}
	}


	// Statická časť.

	private final static String svgZápalka =
		/*"<svg width=\"12\" height=\"65\" version=\"1.1\" " +
			"viewBox=\"0 0 12 65\">\n" +
			"\t<rect x=\"4\" y=\"13\" width=\"4\" height=\"50\" " +
				"fill=\"#cc0\"/>\n" +
			"\t<ellipse cx=\"6\" cy=\"8\" rx=\"4\" ry=\"6\" " +
				"fill=\"#a00\"/>\n" +
		"</svg>";*/

		"<svg width=\"12\" height=\"65\" version=\"1.1\" viewBox=\"" +
			"0 0 12 65\"\n\txmlns=\"http://www.w3.org/2000/svg\">\n" +
			"\t<rect x=\"6.5\" y=\"13\" width=\"1.5\" height=\"50\" " +
				"fill=\"#aa0\"\n\t\tstroke=\"#000\" stroke-width=\"" +
				".5\"/>\n" +
			"\t<rect x=\"4\" y=\"13\" width=\"2.5\" height=\"50\" " +
				"fill=\"#cc0\"\n\t\tstroke=\"#000\" stroke-width=\"" +
				".5\"/>\n" +
			"\t<ellipse cx=\"6\" cy=\"8\" rx=\"4\" ry=\"6\" " +
				"fill=\"#800\"\n\t\tstroke=\"#000\" stroke-width=\"" +
				".5\"/>\n" +
			"\t<ellipse cx=\"5.5\" cy=\"7.5\" rx=\"3\" ry=\"5\" " +
				"fill=\"#a00\"/>\n" +
			"\t<ellipse cx=\"4.375\" cy=\"4.625\" rx=\".875\" " +
				"ry=\".875\"\n\t\tfill=\"#fff\" fill-opacity=\"" +
				".5\"/>\n" +
		"</svg>";

	private static void spracujSVG()
	{
		int n;
		if (-1 != (n = svgPodpora.pridajSVG(svgZápalka)))
		{
			Bod stred = svgPodpora.stredKresby();

			AffineTransform posun = AffineTransform.
					getTranslateInstance(-stred.polohaX(), stred.polohaY());

			int počet = svgPodpora.počet();

			for (int i = 0; i < počet; ++i)
				svgPodpora.pridajTransformácie(i, posun);
		}
	}


	public static void main(String[] args)
	{
		použiKonfiguráciu("Nim.cfg");
		spracujSVG();
		new Nim();
		if (prvéSpustenie()) zbaľ();
	}
}