Diferencia entre revisiones de «PrInf15: Juego: acertar en un polígono»

De MateWiki
Saltar a: navegación, buscar
(Comprobar si un punto está dentro de un polígono)
(Representar y dibujar un polígono)
 
(No se muestran 21 ediciones intermedias del mismo usuario)
Línea 32: Línea 32:
 
Por ejemplo, el triángulo de la derecha está formado por los tres puntos siguientes:
 
Por ejemplo, el triángulo de la derecha está formado por los tres puntos siguientes:
 
<math>\begin{matrix}
 
<math>\begin{matrix}
P1:& (0, 0)\\
+
P_1:& (0, 0)\\
P2:& (0.5, 1)\\
+
P_2:& (0.5, 1)\\
P3:& (1, 0)\\
+
P_3:& (1, 0)\\
 
\end{matrix}</math>
 
\end{matrix}</math>
 
Sin embargo, para poder representarlo con el comando ''fill'' tenemos que separar las coordenadas ''x'' e ''y'' de cada punto. El orden en el que ponemos las coordenadas es importante, ya que tanto el vector ''x'' como el vector ''y'' tienen que coincidir en sus posiciones (de lo contrario, estaríamos representando un polígono diferente):
 
Sin embargo, para poder representarlo con el comando ''fill'' tenemos que separar las coordenadas ''x'' e ''y'' de cada punto. El orden en el que ponemos las coordenadas es importante, ya que tanto el vector ''x'' como el vector ''y'' tienen que coincidir en sus posiciones (de lo contrario, estaríamos representando un polígono diferente):
Línea 42: Línea 42:
 
fill(x, y, 'g'); % Lo pinta de color verde, g viene de green|lang="matlab"}}
 
fill(x, y, 'g'); % Lo pinta de color verde, g viene de green|lang="matlab"}}
 
El comando anterior dibujaría el triángulo relleno de color verde. La forma del triángulo es similar a la mostrada en la figura de la derecha.
 
El comando anterior dibujaría el triángulo relleno de color verde. La forma del triángulo es similar a la mostrada en la figura de la derecha.
 +
{{ Tarea | ¿Podrías dibujar un pentágono relleno de color rojo usando el comando ''fill''? }}
 +
 
=== Comprobar si un punto está dentro de un polígono ===
 
=== Comprobar si un punto está dentro de un polígono ===
 
Para comprobar si un punto dado cae dentro o fuera de un polígono, podemos usar la función ''inpolygon''.
 
Para comprobar si un punto dado cae dentro o fuera de un polígono, podemos usar la función ''inpolygon''.
Línea 55: Línea 57:
 
{{Tarea | Ejecuta los comandos anteriores. ¿De qué tipo es el valor que devuelve la función ''inpolygon''?}}
 
{{Tarea | Ejecuta los comandos anteriores. ¿De qué tipo es el valor que devuelve la función ''inpolygon''?}}
 
{{Tarea | El punto ''(0,0)'' es uno de los vértices del triángulo. Según la función ''inpolygon'', ¿está dentro o fuera del triángulo?}}
 
{{Tarea | El punto ''(0,0)'' es uno de los vértices del triángulo. Según la función ''inpolygon'', ¿está dentro o fuera del triángulo?}}
 +
Conociendo ''fill'' e ''inpolygon'' sería ya posible implementar el juego pedido en esta práctica. En la siguiente sección vamos a proporcionar una primera versión del juego. En esta versión, no hay límite de intentos para el jugador. Puedes intentar hacer tú esta versión del juego antes de pasar a la próxima sección.
 
=== Primera versión del juego ===
 
=== Primera versión del juego ===
 +
Como hemos hecho hasta ahora, vamos a estructura el programa en tres partes:
 +
* Entrada de datos
 +
* Algoritmo
 +
* Salida de datos
 +
En este programa, la '''entrada de datos''' es el punto que pedimos al usuario, y que probaremos para ver si está dentro del polígono. El polígono lo incluiremos nosotros directamente en el código fuente, aunque también podríamos pedir los vectores de coordenadas al usuario. Pero por hacer el programa más sencillo para los usuarios, lo fijaremos nosotros en el código fuente.
 +
 +
El '''algoritmo''' consiste en comprobar si el punto está dentro o fuera del polígono. Si está fuera, hay que pedir otro punto al usuario, hasta que acierte con un punto que está dentro. Como podemos comprobar, el algoritmo tendrá una parte de entrada de datos también, ya que en caso de que el usuario no acierte, tiene que preguntar nuevamente otro punto.
 +
 +
En cuanto a la '''salida de datos''', cuando el jugador haya acertado le mostraremos un mensaje diciendo el número de intentos que ha necesitado para acertar con un punto dentro del polígono.
 +
 +
Vayamos con la primera parte del programa. Como tenemos que preguntar un punto, vamos a pedir al usuario que los introduzca como un vector, usando corchetes. De este modo, podemos preguntar las dos coordenadas usando un único comando ''input'' (si no, tendríamos que preguntar cada coordenada por separado):
 +
{{#tag:source|%% Juego: acertar en un polígono
 +
%% Versión: 0.1
 +
%% Fecha: Agosto de 2013
 +
 +
% Entrada de datos
 +
p = input('Introduce el punto de la forma [x y]: ');
 +
% Vértices del polígono
 +
x = [0 0.5 1];
 +
y = [0 1 0];|lang="matlab"}}
 +
Como podemos ver, hemos añadido al inicio algunos comentarios que identifican al programa. Estos comentarios pueden ser útiles cuando estemos leyendo código que hemos escrito hace tiempo. Es muy útil asignar un número de versión a cada programa que hagamos, para saber si lo hemos modificado. La fecha es también importante, ya que nos hace recordar cuándo escribimos el código. También puedes añadir tu nombre, por si distribuyes el código por Internet.
 +
 +
Vamos ahora a programar el algoritmo. En el algoritmo, tenemos que llevar un contador del número de tiradas, y comprobar si el punto está dentro del polígono. En esta versión, vamos a inicializar ''tiradas'' con el valor ''1'', por la manera en la que vamos a implementar el algoritmo. En otras implementaciones alternativas, podríamos haber inicializado la variable ''tiradas'' a ''0''. Si no lo está, hay que preguntar otro punto al usuario:
 +
{{#tag:source |
 +
% Algoritmo
 +
tiradas = 1;
 +
xj = p(1); % Coordenada x del punto del jugador
 +
yj = p(2); % Coordenada y del punto del jugador
 +
 +
while ~inpolygon(xj, yj, x, y)
 +
  disp('El punto está fuera del polígono. Inténtalo de nuevo.');
 +
  tiradas = tiradas + 1;
 +
  p = input('Introduce el punto de la forma [x y]: ');
 +
  xj = p(1);
 +
  yj = p(2);
 +
end|lang="matlab"}}
 +
Como vemos, usamos el operador ''~'' para negar el resultado de ''inpolygon''. Literalmente, estamos repitiendo el bucle ''mientras el punto no esté en el polígono''. Si el usuario no ha acertado a la primera, le mostramos un mensaje informativo y le pedimos otro punto. Es muy importante actualizar las variables ''xj'' e ''yj'' dentro del bucle. De lo contrario, el bucle estaría continuamente usando los valores que introdujo el usuario al principio del programa.
 +
 +
Una vez que el usuario haya introducido un punto dentro del polígono, la condición del bucle se volverá falsa, y el programa continuará justo después del bucle. La variable ''tiradas'' se ha estado incrementando cada vez que el usuario fallaba, así que ya sabemos el número de intentos que ha necesitado. Vamos a programar entonces la salida de datos:
 +
{{#tag:source|% Salida de datos
 +
fprintf('Has acertado en %d intentos!\n', tiradas);|lang="matlab"}}
 +
{{ Tarea | Comprueba que el programa anterior funciona como se describe en este guión de prácticas. Ejecuta el programa, probando varios puntos que no estén dentro, y luego uno que esté dentro. Luego ejecuta de nuevo el programa, con un punto que acierte en la primera tirada.}}
 +
=== Una versión más gráfica del juego ===
 +
Esta versión es muy similar a la anterior, pero vamos a añadirle una parte gráfica, para que el jugador pueda ver el polígono y sus intentos. En la línea 10 borramos la ventana de gráficos para asegurarnos que no contiene ningún resto de un gráfico anterior. En las líneas 11 y 12 dibujamos el polígono, y ejecutamos ''hold on'' para que todos los gráficos que se dibujen después se superpongan con el actual.
 +
 +
La línea 19 dibuja el primer intento introducido por el usuario, y en la línea 27 se dibuja cada uno de los nuevos intentos.
 +
 +
La ventana de gráficos aparecerá justo después de introducir el primer punto, y cada vez que introduzcamos un nuevo punto. Podemos cerrarla cuando ya la hayamos visto, y aparecerá de nuevo con todos los contenidos tras cada nuevo intento.
 +
{{ Tarea | Ejecuta esta versión del programa usando las mismas entradas que en los ejemplos anteriores, y comprueba gráficamente que cuando el bucle termina el punto está siempre dentro del polígono.}}
 +
{{ matlab | codigo =
 +
%% Juego: acertar en un polígono
 +
%% Versión: 0.2 (ahora con gráficos)
 +
%% Fecha: Agosto de 2013
 +
 +
% Entrada de datos
 +
p = input('Introduce el punto de la forma [x y]: ');
 +
% Vértices del polígono
 +
x = [0 0.5 1];
 +
y = [0 1 0];
 +
clf; % Borra la ventana de gráficos
 +
fill(x, y, 'g'); % Dibuja el polígono en color verde
 +
hold on; % Dibuja siempre encima del gráfico actual
 +
 +
% Algoritmo
 +
tiradas = 1;
 +
xj = p(1); % Coordenada x del punto del jugador
 +
yj = p(2); % Coordenada y del punto del jugador
 +
% Dibuja el punto con una cruz, con una línea de grosor 8
 +
plot(xj, yj, 'x', 'LineWidth',8);
 +
 +
while ~inpolygon(xj, yj, x, y)
 +
  disp('El punto está fuera del polígono. Inténtalo de nuevo.');
 +
  tiradas = tiradas + 1;
 +
  p = input('Introduce el punto de la forma [x y]: ');
 +
  xj = p(1);
 +
  yj = p(2);
 +
  plot(xj, yj, 'x', 'LineWidth',8); % Dibuja el nuevo intento
 +
end
 +
 +
% Salida de datos
 +
fprintf('Has acertado en %d intentos!\n', tiradas);
 +
hold off;
 +
}}
 +
 +
=== Versión con límite de intentos ===
 +
En los programas anteriores, el usuario tiene que seguir introduciendo puntos de manera indefinida hasta que acierte dentro del polígono. Si nunca acierta dentro del polígono, el programa seguirá pidiendo puntos y nunca terminará. Un programa debería intentar terminar siempre. En este caso, debería terminar tras varios intentos fallidos.
 +
{{ Tarea | Modifica el programa anterior, para que si el usuario consume tres intentos sin acertar, termine el bucle usando ''break''. Actualiza también la salida de datos para informar al usuario de que ha perdido por agotar tres intentos, en caso de que no acierte.}}
 +
En realidad, no es necesario usar ''break'' para terminar el bucle en caso de que agote el número de intentos. También podríamos modificar la condición del programa usando un operador lógico, para que el bucle solo continúe mientras no acierte, pero tampoco haya consumido el límite de intentos. En la práctica [[PrInf14: Interrumpir bucles while]] veíamos un ejemplo que usaba ''break''; en el ejercicio post-práctica se pedía eliminar el ''break'' usando un operador lógico.
 +
{{ Tarea | Quita el ''break'' del programa que acabas de escribir, y modifica la condición del bucle para que el comportamiento del programa no cambie. Es decir, si el usuario no acierta en tres intentos, no debería seguir pidiendo puntos y debería mostrar el mensaje diciendo que ha perdido al agotar todos los intentos disponibles.}}
  
 
== Ejercicio post-práctica ==
 
== Ejercicio post-práctica ==
 +
En el programa que hemos escrito en esta práctica, en cualquiera de sus versiones hay algunas partes que están duplicadas:
 +
* el comando ''input'' que le pide el punto al usuario
 +
* las variables ''xj'' e ''yj'' que recogen las coordenadas de ''p''
 +
* el comando ''plot'' que dibuja el punto introducido por el usuario
 +
Esta duplicación del código no es una buena característica. Por ejemplo, si decidiéramos cambiar cómo se dibuja el punto, tendríamos que tocar en dos sitios del programa. Es fácil que se nos olvide tocar en algún sitio, y que tengamos entonces un programa inconsistente.
 +
 +
Es posible simplificar el programa usando una variable bandera. Un posible nombre para esta bandera puede ser ''estaDentro''. Tomaría el valor ''true'' si el punto está dentro, y ''false'' si no lo está, mediante asignación de la salida de ''inpolygon''.
  
 +
Si modificamos el bucle ''while'' para que use esta bandera, podríamos simplificar el programa para que las partes anteriores solo se escriban en un sitio del programa, que sería dentro del bucle. Esto requeriría que el bucle tuviera que ejecutarse al menos una vez.
 +
{{ Tarea | Para que el bucle se ejecute al menos una vez, ¿cuál sería el valor por defecto que tendríamos que asignar a ''estaDentro'' al inicio del programa?. ¿Sería necesario cambiar el valor de inicialización de ''tiradas'' o no?}}
 +
Es cierto que en este caso, al usar esta variable bandera, estaríamos moviendo la entrada de datos dentro del bucle, por lo que no sería posible separar la entrada de datos y el algoritmo. Por regla general, no es una buena idea mezclar diferentes partes del programa, pero en este caso evita la duplicidad del código, por lo que es la solución preferida.
 +
{{ Tarea | Modifica el programa para que use la variable ''estaDentro'' como bandera, y no contenga líneas duplicadas.}}
 +
Recuerda que en la práctica [[PrInf11: Bucles while]] se mostraban algunos ejemplos de uso de variables bandera con bucles ''while''.
 
[[Categoría:Prácticas de Informática]]
 
[[Categoría:Prácticas de Informática]]

Revisión actual del 12:51 9 ago 2013

Práctica de Informática
Juego: acertar en un polígono
Práctica anterior Siguiente práctica
Este artículo es un guión de prácticas de Informática


En esta práctica vamos a aplicar los conocimientos que hemos adquirido sobre bucles para crear un juego sencillo. Se trata de pedir al usuario un punto, que representa una posición en el plano, y comprobar si el punto está dentro de un polígono. El jugador acierta si proporciona un punto dentro del polígono. Si no acierta dentro del polígono, se le da otra oportunidad. Si alcanza un número límite de intentos, pierde la partida.

1 Requisitos previos

Es importante haber realizado las siguientes prácticas antes de realizar esta práctica:

2 Comandos que se aprenderán en esta práctica

fill inpolygon

3 Contenido de la práctica

Al igual que en la práctica anterior, en este caso usaremos un bucle while que tiene una condición compleja. Para implementar esta condición podemos usar tanto un operador lógico, como un break dentro del bucle. En la práctica usaremos un break, y dejaremos como ejercicio post-práctica la implementación que usa un operador lógico y evita el break.

Este programa tiene dos partes complejas, que no hemos programado hasta ahora:

  • Representar y dibujar un polígono
  • Comprobar si un punto está dentro de un polígono

3.1 Representar y dibujar un polígono

Para representar y dibujar un polígono podemos usar la función fill.

Lapiz.png Tarea: Comprueba cómo se usa la función fill usando help fill


Representación de un punto en el plano
Representación de un triángulo a partir de tres puntos

La función fill acepta tres argumentos de entrada:

  • Un vector con las coordenadas x de los vértices del polígono
  • Un vector con las coordenadas y de los vértices del polígono
  • El color con el que se va a rellenar el polígono

Cada vértice del polígono es un punto en el plano, que tendrá dos coordenadas. Por ejemplo, el punto de la imagen de la derecha tiene como coordenadas [math]x=3[/math] e [math]y=2[/math], y se representa como [math](3,2)[/math]. Si tenemos varios puntos y los unimos, podemos formar un polígono. Por ejemplo, con tres puntos podemos formar un triángulo.

El comando fill se encarga de unir los puntos y dibujar el polígono. Pero tiene una manera peculiar de recibir los puntos que forman el polígono. En vez de recibir una lista de puntos, acepta por un lado la lista de las coordenadas x de esos puntos, y como segundo argumento la lista de las coordenadas y de los mismos puntos. Además, necesita también que especifiquemos el color con el que va a pintar el polígono. Si alguno de estos tres argumentos no es correcto, la función fill no pintará el polígono.

Por ejemplo, el triángulo de la derecha está formado por los tres puntos siguientes: [math]\begin{matrix} P_1:& (0, 0)\\ P_2:& (0.5, 1)\\ P_3:& (1, 0)\\ \end{matrix}[/math] Sin embargo, para poder representarlo con el comando fill tenemos que separar las coordenadas x e y de cada punto. El orden en el que ponemos las coordenadas es importante, ya que tanto el vector x como el vector y tienen que coincidir en sus posiciones (de lo contrario, estaríamos representando un polígono diferente):

x = [0 0.5 1];
y = [0 1 0];

fill(x, y, 'g'); % Lo pinta de color verde, g viene de green

El comando anterior dibujaría el triángulo relleno de color verde. La forma del triángulo es similar a la mostrada en la figura de la derecha.

Lapiz.png Tarea: ¿Podrías dibujar un pentágono relleno de color rojo usando el comando fill?


3.2 Comprobar si un punto está dentro de un polígono

Para comprobar si un punto dado cae dentro o fuera de un polígono, podemos usar la función inpolygon.

Lapiz.png Tarea: Comprueba cómo se usa la función inpolygon con help inpolygon. ¿Cuántos argumentos de salida y entrada tiene esta función?


La función inpolygon espera el polígono en el mismo formato que la función fill, es decir, con los valores de las coordenadas x e y en vectores separados. Además, acepta dos números que representan el punto que vamos a comprobar. Por ejemplo, en el triángulo que hemos representado anteriormente, está claro que el punto [math](0.5, 0.5)[/math] cae dentro del polígono, mientras que el punto [math](3,2)[/math] cae fuera. Veamos el resultado de unas pruebas en la línea de comandos de Octave UPM:

>> x = [0 0.5 1];
>> y = [0 1 0];
>> inpolygon(0.5, 0.5, x, y)
ans =  1
>> inpolygon(3, 2, x, y)
ans = 0

Como vemos, en el caso del punto que está dentro la función devuelve 1, y en el caso del punto que está fuera ha devuelto 0.

Lapiz.png Tarea: Ejecuta los comandos anteriores. ¿De qué tipo es el valor que devuelve la función inpolygon?


Lapiz.png Tarea: El punto (0,0) es uno de los vértices del triángulo. Según la función inpolygon, ¿está dentro o fuera del triángulo?


Conociendo fill e inpolygon sería ya posible implementar el juego pedido en esta práctica. En la siguiente sección vamos a proporcionar una primera versión del juego. En esta versión, no hay límite de intentos para el jugador. Puedes intentar hacer tú esta versión del juego antes de pasar a la próxima sección.

3.3 Primera versión del juego

Como hemos hecho hasta ahora, vamos a estructura el programa en tres partes:

  • Entrada de datos
  • Algoritmo
  • Salida de datos

En este programa, la entrada de datos es el punto que pedimos al usuario, y que probaremos para ver si está dentro del polígono. El polígono lo incluiremos nosotros directamente en el código fuente, aunque también podríamos pedir los vectores de coordenadas al usuario. Pero por hacer el programa más sencillo para los usuarios, lo fijaremos nosotros en el código fuente.

El algoritmo consiste en comprobar si el punto está dentro o fuera del polígono. Si está fuera, hay que pedir otro punto al usuario, hasta que acierte con un punto que está dentro. Como podemos comprobar, el algoritmo tendrá una parte de entrada de datos también, ya que en caso de que el usuario no acierte, tiene que preguntar nuevamente otro punto.

En cuanto a la salida de datos, cuando el jugador haya acertado le mostraremos un mensaje diciendo el número de intentos que ha necesitado para acertar con un punto dentro del polígono.

Vayamos con la primera parte del programa. Como tenemos que preguntar un punto, vamos a pedir al usuario que los introduzca como un vector, usando corchetes. De este modo, podemos preguntar las dos coordenadas usando un único comando input (si no, tendríamos que preguntar cada coordenada por separado):

%% Juego: acertar en un polígono
%% Versión: 0.1
%% Fecha: Agosto de 2013

% Entrada de datos
p = input('Introduce el punto de la forma [x y]: ');
% Vértices del polígono
x = [0 0.5 1];
y = [0 1 0];

Como podemos ver, hemos añadido al inicio algunos comentarios que identifican al programa. Estos comentarios pueden ser útiles cuando estemos leyendo código que hemos escrito hace tiempo. Es muy útil asignar un número de versión a cada programa que hagamos, para saber si lo hemos modificado. La fecha es también importante, ya que nos hace recordar cuándo escribimos el código. También puedes añadir tu nombre, por si distribuyes el código por Internet.

Vamos ahora a programar el algoritmo. En el algoritmo, tenemos que llevar un contador del número de tiradas, y comprobar si el punto está dentro del polígono. En esta versión, vamos a inicializar tiradas con el valor 1, por la manera en la que vamos a implementar el algoritmo. En otras implementaciones alternativas, podríamos haber inicializado la variable tiradas a 0. Si no lo está, hay que preguntar otro punto al usuario:

% Algoritmo
tiradas = 1;
xj = p(1); % Coordenada x del punto del jugador
yj = p(2); % Coordenada y del punto del jugador

while ~inpolygon(xj, yj, x, y)
  disp('El punto está fuera del polígono. Inténtalo de nuevo.');
  tiradas = tiradas + 1;
  p = input('Introduce el punto de la forma [x y]: ');
  xj = p(1);
  yj = p(2);
end

Como vemos, usamos el operador ~ para negar el resultado de inpolygon. Literalmente, estamos repitiendo el bucle mientras el punto no esté en el polígono. Si el usuario no ha acertado a la primera, le mostramos un mensaje informativo y le pedimos otro punto. Es muy importante actualizar las variables xj e yj dentro del bucle. De lo contrario, el bucle estaría continuamente usando los valores que introdujo el usuario al principio del programa.

Una vez que el usuario haya introducido un punto dentro del polígono, la condición del bucle se volverá falsa, y el programa continuará justo después del bucle. La variable tiradas se ha estado incrementando cada vez que el usuario fallaba, así que ya sabemos el número de intentos que ha necesitado. Vamos a programar entonces la salida de datos:

% Salida de datos
fprintf('Has acertado en %d intentos!\n', tiradas);
Lapiz.png Tarea: Comprueba que el programa anterior funciona como se describe en este guión de prácticas. Ejecuta el programa, probando varios puntos que no estén dentro, y luego uno que esté dentro. Luego ejecuta de nuevo el programa, con un punto que acierte en la primera tirada.


3.4 Una versión más gráfica del juego

Esta versión es muy similar a la anterior, pero vamos a añadirle una parte gráfica, para que el jugador pueda ver el polígono y sus intentos. En la línea 10 borramos la ventana de gráficos para asegurarnos que no contiene ningún resto de un gráfico anterior. En las líneas 11 y 12 dibujamos el polígono, y ejecutamos hold on para que todos los gráficos que se dibujen después se superpongan con el actual.

La línea 19 dibuja el primer intento introducido por el usuario, y en la línea 27 se dibuja cada uno de los nuevos intentos.

La ventana de gráficos aparecerá justo después de introducir el primer punto, y cada vez que introduzcamos un nuevo punto. Podemos cerrarla cuando ya la hayamos visto, y aparecerá de nuevo con todos los contenidos tras cada nuevo intento.

Lapiz.png Tarea: Ejecuta esta versión del programa usando las mismas entradas que en los ejemplos anteriores, y comprueba gráficamente que cuando el bucle termina el punto está siempre dentro del polígono.


%% Juego: acertar en un polígono
%% Versión: 0.2 (ahora con gráficos)
%% Fecha: Agosto de 2013

% Entrada de datos
p = input('Introduce el punto de la forma [x y]: ');
% Vértices del polígono
x = [0 0.5 1];
y = [0 1 0];
clf; % Borra la ventana de gráficos
fill(x, y, 'g'); % Dibuja el polígono en color verde
hold on; % Dibuja siempre encima del gráfico actual

% Algoritmo
tiradas = 1;
xj = p(1); % Coordenada x del punto del jugador
yj = p(2); % Coordenada y del punto del jugador
% Dibuja el punto con una cruz, con una línea de grosor 8
plot(xj, yj, 'x', 'LineWidth',8);

while ~inpolygon(xj, yj, x, y)
  disp('El punto está fuera del polígono. Inténtalo de nuevo.');
  tiradas = tiradas + 1;
  p = input('Introduce el punto de la forma [x y]: ');
  xj = p(1);
  yj = p(2);
  plot(xj, yj, 'x', 'LineWidth',8); % Dibuja el nuevo intento
end

% Salida de datos
fprintf('Has acertado en %d intentos!\n', tiradas);
hold off;


3.5 Versión con límite de intentos

En los programas anteriores, el usuario tiene que seguir introduciendo puntos de manera indefinida hasta que acierte dentro del polígono. Si nunca acierta dentro del polígono, el programa seguirá pidiendo puntos y nunca terminará. Un programa debería intentar terminar siempre. En este caso, debería terminar tras varios intentos fallidos.

Lapiz.png Tarea: Modifica el programa anterior, para que si el usuario consume tres intentos sin acertar, termine el bucle usando break. Actualiza también la salida de datos para informar al usuario de que ha perdido por agotar tres intentos, en caso de que no acierte.


En realidad, no es necesario usar break para terminar el bucle en caso de que agote el número de intentos. También podríamos modificar la condición del programa usando un operador lógico, para que el bucle solo continúe mientras no acierte, pero tampoco haya consumido el límite de intentos. En la práctica PrInf14: Interrumpir bucles while veíamos un ejemplo que usaba break; en el ejercicio post-práctica se pedía eliminar el break usando un operador lógico.

Lapiz.png Tarea: Quita el break del programa que acabas de escribir, y modifica la condición del bucle para que el comportamiento del programa no cambie. Es decir, si el usuario no acierta en tres intentos, no debería seguir pidiendo puntos y debería mostrar el mensaje diciendo que ha perdido al agotar todos los intentos disponibles.


4 Ejercicio post-práctica

En el programa que hemos escrito en esta práctica, en cualquiera de sus versiones hay algunas partes que están duplicadas:

  • el comando input que le pide el punto al usuario
  • las variables xj e yj que recogen las coordenadas de p
  • el comando plot que dibuja el punto introducido por el usuario

Esta duplicación del código no es una buena característica. Por ejemplo, si decidiéramos cambiar cómo se dibuja el punto, tendríamos que tocar en dos sitios del programa. Es fácil que se nos olvide tocar en algún sitio, y que tengamos entonces un programa inconsistente.

Es posible simplificar el programa usando una variable bandera. Un posible nombre para esta bandera puede ser estaDentro. Tomaría el valor true si el punto está dentro, y false si no lo está, mediante asignación de la salida de inpolygon.

Si modificamos el bucle while para que use esta bandera, podríamos simplificar el programa para que las partes anteriores solo se escriban en un sitio del programa, que sería dentro del bucle. Esto requeriría que el bucle tuviera que ejecutarse al menos una vez.

Lapiz.png Tarea: Para que el bucle se ejecute al menos una vez, ¿cuál sería el valor por defecto que tendríamos que asignar a estaDentro al inicio del programa?. ¿Sería necesario cambiar el valor de inicialización de tiradas o no?


Es cierto que en este caso, al usar esta variable bandera, estaríamos moviendo la entrada de datos dentro del bucle, por lo que no sería posible separar la entrada de datos y el algoritmo. Por regla general, no es una buena idea mezclar diferentes partes del programa, pero en este caso evita la duplicidad del código, por lo que es la solución preferida.

Lapiz.png Tarea: Modifica el programa para que use la variable estaDentro como bandera, y no contenga líneas duplicadas.


Recuerda que en la práctica PrInf11: Bucles while se mostraban algunos ejemplos de uso de variables bandera con bucles while.