Universidad de Costa Rica
Escuela de Ciencias de la
Computación e Informática
Profesor Adolfo Di Mare
CI-1101
II Semestre 2014
[<=] [home] [<>] [\/] [=>]
CI-1101 Programación I

Tarea #4 [solución]

Juego de toques y famas

      Implemente el método estático convierte() para completar el programa ToqueFama.java que sirve para jugar el juego de "Toques y famas" jalando las adinanzas del teclado. Demuestre que su método funciona corriendo la prueba testConvierte() en el programa de prueba de la tarea programada anterior.

      Para esta tarea programada usted debe enviarme estos archivos:

  1. Encajados.java
  2. TestEncajados.java
  3. ToqueFama.java
  4. CARNET.docx
  5. CARNET.html
  6. CARNET.url

      En el documento CARNET.docx deben describir su experiencia de aprendizaje para completar esta tarea. Explique qué hizo, cómo lo hizo y por qué lo hizo. En especial, es importante que explique cómo funciona su programa, indicando cuál es el trabajo que realiza cada uno de los módulos que conforman su solución. El archivo CARNET.html es copia de la página Internet en donde usted instaló la solución de su tarea programada. El archivo de enlace Internet CARNET.url contiene solo 2 renglones y sirve para abrir la página Internet en que está la documentación completa de su programa (para crearlo puede usar cualquier editor de texto).

[InternetShortcut]
URL=http://www.di-mare.com/adolfo/cursos/ci-1101.htm
ci-1101.url

      Construya su solución en 2 pasos. Primero, complete la implementación de la rutina convierte() de manera que produzca los resultados definidos en su especificación. No olvide determinar si su rutina convierte() funciona correctamente, probándola con testConvierte(), que es el bloque de código que usted debe agregarle al programa testEncajados.java que usó en la tarea programada anterior.

      Después de comprobar que su rutina convierte() funciona correctamente, úsela para completar el programa ToqueFama.java, agregando en el bloque que está marcado con la indicación "{ *** RELLENE CON SU ALGORITMO *** }" todas las instrucciones para leer adivinanzas y cotejarlas con el número secreto almacenado en el vector SECRETO[]. Es aquí adonde debe invocar convierte() para almacenar la siguiente adivinanza en el vector ADIVINA[], pues la rutina estanEncajados() trabaja con valores almacenados en vectores. ¡No modifique ninguna otra parte del programa!

Consulta:
Profe: hay algo que no funciona en el programa ToqueFama.java porque para un SECRETO[] de "n" dígitos, siempre genera el mismo secreto:
Respuesta:
En le programa principal ToqueFama.java está declarada la variable "semilla" así:
  long semilla = 123456789;
      Usualmente, ese valor debe tener un número loco, que garantice que en cada ejecución el juego sea diferente. Sin embargo, para esta tarea es mejor que el programa se comporte siempre de la misma forma, pues de lo contrario cuesta más depurarlo.
      Por eso, decidí dejar fija la semilla. Sin embago, después de que terminen de hacer el programa, me imagino que ustedes querrán cambiarlo para que sea más divertido jugarlo (podrían inicializar la semilla con la hora actual, o con el día, etc.).
Consulta:
Profe, ¿tiene el programa ya ejecutable resuelto? Es que quiero ver cómo debe funcionar.
Respuesta:
Si quieren jugar, pueden usar esta versión que está en la red:
- http://www.web-games-online.com/mastermind/

Esto lo obtuve con el programa que ustedes deben completar:
¿Cuántos dígitos quiere en SECRETO? 5

Adivina> 00000
4 toques 1 famas

Adivina> 11111
4 toques 1 famas

Adivina> 22222
4 toques 1 famas

Adivina> 33333
4 toques 1 famas

Adivina> 44444
4 toques 1 famas

Adivina> 55555
0 toques 0 famas

Adivina> 01234
5 toques 0 famas

Adivina> 12340
3 toques 2 famas

Adivina> 23401
3 toques 2 famas

Adivina> 34012
5 toques 0 famas

Adivina> 40123
4 toques 1 famas

Adivina> 20341

Duraste solo 12 tiros

      Entregue su tarea por correo electrónico, como lo hizo anteriormente.


/***********************************************************\
* Incluya este bloque de código en el módulo Encajados.java *
\***********************************************************/

/* Almacena los últimos {@code n} dígitos de {@code adivina} en el vector {@code ADIVINA[]}.
 * Si hay más digitos al principio de {@code adivina}, simplemente los ignora.
 * En caso de que falten dígitos, los rellena con el valor cero {@code 0}.
 * Solo modifica sus argumentos cuando el valor de {@code n} está en el rango {@code [1..9]}.
 * Solo modifica sus argumentos si ya ocurre que {@code ADIVINA.length == n}.
 */
public static void convierte( int n, int adivina, int ADIVINA[] ) {
    {    
        /******************************\
        *                              *
        *   RELLENE CON SU ALGORITMO   *
        *                              *
        \******************************/
        /*  VEC[]
            +---+---+---+---+---+---+---+---+---+---+
            | C | A | L | A | M | B | R | I | N | A |
            +---+---+---+---+---+---+---+---+---+---+
              0   1   2   3   4   5   6   7   8   9   10
              i ->                             <- j

            assertTrue( VEC[0] == 'C' );
            // ...
            assertTrue( VEC[4] == 'M' );
            // ...
            assertTrue( VEC[9] == 'A' );

            assertTrue( VEC.length == 10 );

            Rango para el ciclo:
            - [ 0..9 ]
            - [ 0 .. VEC.length-1 ]

            - VEC[0] es el primero
            - VEC[ VEC.length-1 ] es el último

            - VEC[ VEC.length ] es incorrecto
            - El índice [ VEC.length ] está fuera de rango

            - Si N == VEC.length ==> N-1 == (VEC.length-1)
              ...
            - Si N == VEC.length ==> N-3 == (VEC.length-1) - 2
        */
    }
}

/***************************************************************\
* Incluya este bloque de código en el módulo TestEncajados.java *
\***************************************************************/

/** Verifica que los vectores {@code L[]} y {@code R[]} tengan exactamente los mismos valores */
public static boolean isEqual( int L[] , int R[] ) {
    if ( L.length != R.length ) {
        return false;
    }
    for ( int i=0; i<L.length; ++i ) {
        if ( L[i] != R[i] ) {
            return false;
        }
    }
    return true;
}

/** test -> {@code Encajados.convierte()}. */
public void testConvierte() {
    int V[] = { 0 , 1 , 2 , 3 }; // vector de pruebas

    Encajados.convierte( 0, 1234567, V ); // n == 0
    assertTrue( isEqual( V , A0123 ) );   // no cambia V[]

    assertTrue( A1111.length == 4 );
    assertTrue( V.length     != 2 );

    Encajados.convierte( 4,  1111, V );
    assertTrue( isEqual( V, A1111 ) );

    Encajados.convierte( 4, 990123, V );
    assertTrue( isEqual( V,  A0123 ) );

    Encajados.convierte( 4,   123, V );
    assertTrue( isEqual( V, A0123 ) );

    V = new int[1];
    Encajados.convierte( 1, 139, V );
    assertTrue( V[0] == 9 && V.length == 1 );

    V = new int[2];
    Encajados.convierte( 2,  01, V );
    assertTrue( isEqual( V, A01 ) );

    V = new int[6];
    Encajados.convierte( 6,  990123, V );
    assertTrue( isEqual( V, A990123 ) );
    {
        int R[] = { 5,1 };
        Encajados.convierte( 2,  01, R );
        assertFalse( R[0]==5 && R[1]==1 );
        assertTrue(  isEqual( R, A01 ) );
        assertTrue(  R[0]==0 && R[1]==1 );
    }
    {
        int R[] = { 3 };
        Encajados.convierte( 2,  11, R );
        assertTrue( R[0]==3 && R.length != 2 );

        Encajados.convierte( 1,  0, R );  assertTrue( R[0]==0 );
        Encajados.convierte( 1,  9, R );  assertTrue( R[0]==9 );
        Encajados.convierte( 1, 90, R );  assertTrue( R[0]==0 );
    }
    {
        int R[] = new int[33];
        Encajados.convierte( R.length,  1, R );
        assertTrue(  R[0]!=1 && R.length>9 );
    }
}
/**
    @(#)ToqueFama.java 2009
    Juego de Toques y Famas (<em>Master Mind</em>).
    @author Adolfo Di Mare <adolfo@di-mare.com>
*/

import java.io.*; // Clases para leer desde el teclado
import java.util.Random; // Clases para números aleatorios
import java.lang.Character; // isDigit()

/** Clase que contiene el programa principal {@code main()}. */
public class ToqueFama {
    
    /** Lee un número de {@code CIN}.
      * Se salta los caracteres no númericos antes del número.
      */
    public static int leeInt( BufferedReader CIN ) {
        int num = 0; char ch;
        try {
            do { // se salta la basura antes del número
                ch = (char)CIN.read();
            } while ( ! Character.isDigit(ch) );

            while ( Character.isDigit(ch) ) {
                num = num*10 + (ch-'0');
                ch = (char)CIN.read();
            }
            return num;
        }
        catch (IOException e) {
            return 0;
        }
    }

    /** Genera el vector {@code SECRETO[]} de longitud "{@code n}" de adivinanzas.
      * <ul><li> {@code (n == SECRETO.length)}. </li>
      * <li> Los valores del vector {@code SECRETO[]} siempre están en el rango el
      *     {@code [0..n-1]}.</li>
      * <li> Ningún valor del vector está repetido.</li>
      * <li> El generador de valores aleatorios se incializa con {@code semilla}.</li>
      * <li> Para el juego de Toques y Famas resulta mejor que el número secreto
      *     siempre tenga dígitos diferentes. </li>
      * <li> Por ejemplo, para {@code n==7}, esta rutina usa el vector
      *     {@code {0,1,2,3,4,5,6}} para luego revolverle los números de manera
      *     aleatoria, de forma resulte algo como {@code {3,1,5,6,0,2,4}}, en donde
      *     aparecen todos los dígitos en el rango {@code [0..7-1]} pero sin
      *     repeticiones. </li>
      * </ul>
      */
    public static void generaSecreto( long semilla, int SECRETO[] ) {
        Random rand = new Random( semilla );
        final int n = SECRETO.length;
        // Rellena SECRETO[] con los primeros números
        for ( int i=0; i<n; ++i ) {
            SECRETO[i] = i;
        }
        // Reacomoda los valores de SECRETO[] usando números aleatorios
        for ( int i=0; i<n; ++i ) {
            int j = rand.nextInt(n); // "j" nunca está fuera de rango
            if ( i != j ) { // intercambia
                int temp = SECRETO[i];
                SECRETO[i] = SECRETO[j];
                SECRETO[j] = temp;
            }
        }
    }

    /** Nombre el índice de "exactos" en el vector {@code res[]} */
    static final int EXACTO  = Encajados.EXACTO;
    /** Nombre el índice de "corridos" en el vector {@code res[]} */
    static final int CORRIDO = Encajados.CORRIDO;

    /** {@code n*DUREZA} es la cantidad máxima de adivinanzas del juego */
    static final int DUREZA  = 7;

    /** Programa principal.
      * OJO: tira la excepción <code>IOException</code> cuando {@code CIN} no funciona.
      */
    public static void main( String args[] ) throws IOException {
        // CIN es el lector de la consola (teclado)
        BufferedReader CIN = new BufferedReader( new InputStreamReader(System.in) );
        System.out.print( "¿Cuántos dígitos quiere en SECRETO? " );
        int n = leeInt(CIN) % 9; // lee el nuevo valor
        if ( n <= 0 ) { // control de datos de entrada
            n = 4;
        }
        long semilla = 123456789;
        int SECRETO[] = new int[n]; // este es el que hay que adivinar
        int ADIVINA[] = new int[n]; // siguiente adivinanza
        int res[] = { -1 , -1 };    // res[] tiene dimensión 2
        generaSecreto( semilla, SECRETO );

        // ciclo para procesar cada adivinanza
        int tiros = 0;
        System.out.print( "\nAdivina> " );
        while ( tiros != n*DUREZA ) {
            tiros++;
            int adivina = leeInt(CIN); // jala la adivinanza
            {    
                /******************************\
                *                              *
                *   RELLENE CON SU ALGORITMO   *
                *                              *
                \******************************/
            }
        }

        if ( tiros == n*DUREZA ) {
            System.out.print( "\n¡¡¡ CHAPA !!!\n" );
        }
        else {
            System.out.print( "\nDuraste solo " + (tiros) + " tiros\n" );
        }

        CIN.close(); // Cierra la consola de lectura
        return;
    }  // main()
} // ToqueFama

/* EOF: ToqueFama.java */

Soluciones

[mailto:] Adolfo Di Mare <adolfo@di-mare.com>.
Copyright © 2014
Derechos de autor reservados © 2014
[home] <> [/\]