MacBook 2007 (A1181): Cambiar pasta térmica y teclado

Estoy muy contento con el MacBook que compré y la verdad es que, tras actualizarlo, todavía es un equipo que puede sacarte de más de un apuro.
Sin embargo me quedaban dos tareas pendientes para dejar este equipo perfecto: una limpieza interna a fondo y sustituir toda la parte superior de la carcasa por otra nueva.

Una pista de que el portátil necesitaba la limpieza eran las temperaturas que podía ver en MacOS mediante iStat Menus, o en Windows con CoreTemp. Sin haber hecho apenas nada, solo arrancando el equipo, el procesador estaba a unos bastante preocupantes 75ºC y solo bajaba hasta 57º después de unos cuantos minutos sin hacer nada.

El ventilador estaba corriendo de forma bastante ruidosa la mayor parte del tiempo y sospechaba que jamás se le había cambiado la pasta térmica al procesador o a la tarjeta gráfica. Desmontar el equipo es un poco más laborioso que en el MacBook Pro, ya que es un equipo anterior a los Unibody, pero se puede hacer con un sencillo destornillador de estrella. Me apoyé también mucho algunos videos de youtube, como éste, hasta tener el equipo finalmente desmontado ante mí.

Para la limpieza empleé un pequeño pincel y quité todo el polvo que cubría los componentes. A continuación, usé un paño de microfibra empapado en alcohol para retirar todos los restos de la antigua pasta térmica del procesador y la tarjeta gráfica hasta dejarlos relucientes:

Ésta es una parte muy importante porque aquí debe producirse la primera transferencia de calor: por contacto entre el procesador y el disipador. Éste lo conducirá hasta el radiador, se radiará al aire que hay a su alrededor y es el único ventilador del equipo el que debe encargarse de expulsar ese aire caliente hacia el exterior.
Una vez quité la pasta original ya reseca y solidificada, apliqué una pasta térmica nueva. Me han recomendado la Thermal Grizzly Kryonaut, que también empleé en el MacBook Pro cuando lo abrí para cambiar el SSD.
También hice una limpieza profunda en las aspas del ventilador y las láminas del radiador antes de volver a colocarlos.

Éste portátil fue comprado en EE.UU. en su momento, por lo que venía con un teclado tipo ANSI. Esto se puede reconocer fácilmente por la forma de la tecla Intro, o por el hecho de que no hay ninguna tecla entre la letra Z y May. Izq.

Los teclados ANSI suelen ser bastante valorados entre los programadores porque tienen un acceso cómodo a símbolos muy comunes en todos los lenguajes sin necesidad de utilizar combinaciones de teclas. A mi en particular no me preocupaba en absoluto la ausencia de la letra Ñ o la disposición de los signos de puntuación, acentos, llaves, etc… pero es cierto que en el día a día cometo más equivocaciones porque estoy acostumbrado a los teclados ISO españoles, los más comunes.
Según iFixit, se puede sustituir toda esta parte de la carcasa y conectar un teclado de diferente tipo sin ningún problema. Además, ya que el ordenador acusaba desconchados en el borde del plástico (algo muy común en esta generación de Macs), preferí lanzarme a la búsqueda de un repuesto con teclado español. Sinceramente, la búsqueda es complicada. No hay repuestos originales, obviamente, y por eBay se pueden encontrar algunos, pero o bien se encuentran en muy mal estado, o corresponden a otro idioma.
Sin embargo pude encontrar una cubierta con teclado español en un estado razonablemente bueno, así que simplemente enchufé ese y volví a cerrar el equipo, que ahora tiene incluso un mejor aspecto.

Había bastante polvo y suciedad acumulados tras 14 años en el interior del equipo y la pasta térmica se había degradado tanto, que esperaba ver una sustancial diferencia en el funcionamiento del equipo. Así sucedió:

De un plumazo, el equipo arrancó y la temperatura se estabilizó en unos frescos 36ºC. Haciendo algunas tareas durante un rato, subió a los 45-50º:

Esto significa que el ordenador estaba funcionando con una diferencia de 20ºC tras arrancar y unos 10-15ºC menos durante una carga de trabajo moderada. Realmente impresionante.

Lo mejor es que automáticamente el MacBook se volvió una máquina mucho más silenciosa ya que ahora no suele superar los 60ºC. Si lo hace, el ventilador empieza a girar a pocas revoluciones y su velocidad de giro aumenta si la temperatura lo sigue haciendo. Al estar en general por debajo de ese umbral, trabajar en él es una experiencia mucho más placentera.

Java: Tres En Raya

Continuando con la senda del reciente post sobre el juego del ahorcado, esta vez vengo a compartir mi versión del tres en raya.
Está desarrollado sin muchas florituras a nivel de algoritmo; pero puede valer para repasar conceptos ya vistos.

La clase Tres En Raya
Al principio definiremos algunas cosas que vamos a necesitar. Nuestro tablero de juego no es más que un vector de 9 caracteres. Esas nueve casillas que representa pueden estar vacías (un espacio en blanco) o ocupadas por el carácter con el que juega el usuario (charUsuario=’x’) o la máquina (charMáquina=’o’). También aquí tenemos los contadores de victorias y derrotas.

Por otra parte, el método main es prácticamente calcado al visto en el juego del ahorcado. Simplemente muestra un menú y ejecuta las acciones pertinentes:

Por supuesto importaremos las clases necesarias para el Scanner y el tratamiento de excepciones.

El tablero
Para mostrar el tablero a lo largo del juego voy a desarrollar tres métodos. Al empezar una partida, quiero que se le muestre al jugador un tablero con los números de cada casilla (del 1 al 9), que son los números de casilla que introducirá por teclado para saber dónde quiere colocar sus fichas.
Después, ese tablero deberá estar vacío (introduciendo caracteres ‘ ‘) para poder empezar y se mostrará su estado paso a paso conforme se vayan realizando movimientos hasta terminar la partida.

No hay nada especial en estos tres métodos salvo convertir un número a un carácter mediante la concatenación de dos métodos: Integer.toString(numero).charAt(0). Prácticamente auto-explicativo.

Desarrollo
He tratado el desarrollo del juego en dos métodos para hacerlo más legible. Sigo un modelo Top-Down así que el primero que veremos es “jugar()”, que es el que se invoca desde la opción 1 del menú. Básicamente cuando el usuario escoge jugar, se le muestra el tablero numerado y se vacía.

Este método a su vez invoca a “partida()”, donde se desarrolla la partida, y que devuelve un char correspondiente a quien haya obtenido la victoria o en caso de empate. Aquí se actualizan los contadores si es el caso.
Ahora bien, para decidir quien empieza a colocar sus fichas he creado un método auxiliar “turnoInicial()” muy sencillo donde se sortea de forma más o menos aleatoria el turno. Requiere importar la clase Random.

Y ya estamos listos para comenzar la partida.
El bucle de jugadas se ejecutará mientras no haya un ganador y mientras el numero de fichas introducidas en el tablero sea menor que 10.
Primero mostramos el estado actual del tablero, preguntamos si aún no se han introducido todas las fichas posibles y según de quien sea el turno, introduciremos una ficha del jugador o de la máquina. En el caso de que sea turno del jugador, simplemente comprobamos que la posición indicada está vacía y le preguntamos de nuevo si no lo está. Si es el turno de la máquina, llamamos al método “ocuparCasilla()”, del que hablaré más tarde.
Una vez colocada la ficha, cambiamos el turno, comprobamos si hay un ganador con el método “victoria()” y aumentamos el contador de fichas ya introducidas.
Una vez que el bucle acabe, mostramos el estado final del tablero y devolvemos el char correspondiente al ganador (o un carácter vacío si ha sido empate).

Casillas y victorias
No hablo de estadísticas del Real Madrid ni de la Selección Española. ¿Como decide la máquina qué casillas ocupar para buscar la victoria? Hay varias formas sencillas de hacer esto pero me temo que ninguna satisfactoria. Buscando información en internet llegué al algoritmo minimax, pero no quería complicarme mucho para implementarlo.
De modo que la máquina hará una serie de comprobaciones para que parezca que juega de forma inteligente cuando en realidad no lo hace.
La primera y más evidente se trata de comprobar la casilla central (posición 4 del vector). Si está libre, la ocupa.
Si ya estaba ocupada por una ficha suya, la máquina tratará entonces de buscar las casillas 1, 3, 5 y 7 para formar la línea central vertical u horizontal. Si después de comprobarlas, no las ha podido ocupar, entonces buscará cualquiera de las restantes simplemente iterando a lo largo de todo el vector.
Si la posición central pertenece al jugador, no obstante, la máquina realiza un proceso análogo buscando jugar en los vértices del tablero, es decir, las posiciones 0, 2, 6 y 8, para formar una de las dos líneas diagonales.

No se trata, ni mucho menos, de una “inteligencia artificial”. Más bien se trata de un “primitivo artificio intelectual”.

Como después de introducir una ficha, hay que comprobar si tenemos un ganador, lo que he hecho es el método “victoria()”, que simplemente aglutina una serie de comprobaciones para verificar si existen tres fichas en raya en alguna de las verticales, horizontales u oblicuas.

De nuevo, es un mecanismo muy rudimentario, pero suficiente.

Jugando
Compilamos con:
$ javac TresEnRaya.java
Y ejecutamos con:
$ java TresEnRaya
Dejo a continuación tres capturas de como se comporta el programa así como del código completo. Si quieres descargarlo puedes hacerlo desde éste enlace y como siempre, los comentarios del blog están a vuestra disposición para cualquier duda o sugerencia 🙂



Java: El Ahorcado

Hace mucho que no pongo por aquí nada de programación en Java y hoy traigo el juego del ahorcado, que es un ejercicio propuesto en clase y que resulta bastante interesante.
Queremos desarrollar un programa que ejecute el juego del ahorcado. En cada partida presenta la palabra secreta escrita con “_” y se van revelando las letras que el usuario acierta. Se permite cometer hasta 5 fallos como máximo. Las letras que no formen parte de la palabra secreta cuentan como fallos; y si se introduce una letra correcta que ya había sido revelada con anterioridad, también. La partida solo termina si el usuario acierta la palabra o si se han cometido el número máximo de fallos.
Además, he introducido tres elementos extra:
– Un menú con tres opciones: jugar partida, ver estadísticas y salir. El programa se ejecuta hasta que el usuario selecciona la última opción.
– Las estadísticas de partidas ganadas y partidas perdidas.
– El programa cuenta con un conjunto de palabras para jugar. Se escogerán de forma aleatoria pero tratando de evitar que se produzcan repeticiones en la medida de lo posible.
Vamos allá!

El programa principal
Vamos a empezar creando la clase Ahorcado y dos variables que serán el numero de victorias y el de derrotas. En el método main() es donde creo el bucle que muestra el menú, lee la opción que introduce el usuario y la ejecuta.
Para leer texto por teclado estoy empleando la clase Scanner, que pertenece al paquete java.util y que he importado al principio. Es muy cómodo hacerlo de esta manera aunque existen otras, pero tengo a mi disposición el método Scanner.nextInt(), que me devuelve el primer número entero que el usuario teclea.
Hay la posibilidad de que el usuario introduzca algo que no sea un número, por lo que debo encerrar la lectura de la variable opción entre try-catch. No es mi forma favorita de manejar excepciones pero es un caso muy sencillo y se puede hacer aquí directamente. La excepción que debemos controlar es que los tipos no coincidan, o lo que es lo mismo, InputMismatchException, que también pertenece al paquete java.util y debo importar al principio.
Si el try-catch no detecta ninguna excepción, se lee el número que introduce el usuario. Si la detecta pone un número cualquiera, en mi caso el 100.
Es importante que una vez leída la opción (o controlada la excepción), vaciemos el resto del buffer de entrada, simplemente leyendo hasta el final de línea con el método input.nextLine();.

En la sentencia switch(opcion) es donde esa opción se transforma en realidad. Si el usuario ha introducido el número 1, arrancará el método partida(). Debo pasarle el objeto input de la clase Scanner para poder leer las letras que vaya introduciendo, y esperaré a que el método retorne FALSE si el usuario ha perdido o TRUE si ha adivinado la palabra. Dependiendo de ese valor, aumento un contador u otro.
La opción 2 simplemente muestra el valor de éstos y la 0 anuncia el final del programa. La opción default informa de que la opción introducida no es válida. Se llega a este punto tanto si el usuario mete un número incorrecto (p. ej el 5) o cualquier carácter que provoque la excepción que controlaba el try-catch, ya que la variable opción valdría 100.
Por último, cuando el programa termina, invocamos input.close() para cerrar el buffer de entrada.

Las palabras
Durante las partidas vamos a necesitar unas cuantas palabras con las que jugar. Yo he decidido almacenar unas cuantas en un array de Strings al que llamo vPalabrasSecretas. La variable maxIntentos, marcada como final para evitar que se cambie accidentalmente durante el juego, establece el numero máximo de intentos fallidos que tiene un jugador para adivinar la palabra secreta. Si se quiere hacer más fácil el juego, se aumenta este número y listo, no es preciso ningún otro cambio.


También tengo otro array de índices para controlar la repetición de las palabras. La idea es muy sencilla: el tamaño de vIndices es el mismo que el de Strings; si ya he jugado con una palabra, el valor del índice correlativo lo marco como TRUE. Si un índice tiene valor FALSE entonces es que la palabra correlativa en vPalabrasSecretas aun no ha sido seleccionada para jugar.
Por ejemplo: Si juego con la palabra vPalabrasSecretas[0] (“cámara”), pongo TRUE en vIndices[0]. Si después, juego con la palabra “terminal” (que está en la posición 9), pongo vIndices[9] a TRUE.
¿Podría esto resolverse con un único array bidimensional? Sí, perfectamente; pero lo he hecho así para no enrevesarlo mucho. Si se quiere apostar por esa solución, no se requieren grandes cambios.

Con esto definido, voy a crear un método que devuelva una palabra secreta nueva y si es posible con la que no se haya jugado hasta el momento. La forma de hacerlo es calcular el módulo de partidas ya jugadas entre el número de palabras disponibles para jugar. Cuando el juego empieza (0 % vPalabrasSecretas.length = 0) o cuando ya se ha jugado con todas, pondremos todos los valores de vPalabrasJugadas a FALSE.
Después, se pide un numero entero aleatorio (Random().nextInt()) que va de 0 hasta el número de palabras que hay en el vector. Esto se repite todas las veces que sea necesario mientras el índice correspondiente sea TRUE (es decir, mientras las palabras en ese índice ya hayan salido) hasta que encuentre un índice que vale FALSE (una palabra con la que aún no se ha jugado). Cuando hemos encontrado ese índice, se sale del bucle. Lo marcamos como TRUE y devolvemos la palabra en esa posición.

La clase Random debe ser importada, también forma parte del paquete java.util.

Otras operaciones
Dentro del método partida hay dos secuencias de instrucciones que, sin ser complejas, nos conviene programar en métodos aparte. Así facilitamos la limpieza y la comprensión del código del método partida() cuando lleguemos a él.
La primera operación, por obvia, es leerLetra(). Debe recibir el input en los parámetros parámetros de la llamada y devolver un único carácter. Por supuesto, controlaremos las excepciones correspondientes, tal y como hicimos al leer las opciones del menú principal.


Como la clase Scanner no proporciona un método para leer el primer carácter, lo que hacemos es leer toda una línea (nextLine()), convertirla a minúsculas (toLowerCase()), y de ahí tomar el carácter en la posición 0 (charAt());
En la clase Character tenemos un método llamado isLetter() que comprueba si un carácter dado es una letra, y no terminaremos el bucle de pedir letras mientras no obtengamos una.

El siguiente método que he escrito es el que mostrarEstadoPartida(). Básicamente lo que hace es escribir en pantalla tantos “_” como caracteres componen la palabra secreta, o la letra si ésta ya ha sido revelada por el jugador.
A continuación, escribe las letras que no forman parte de la palabra o las que ya se revelaron previamente, porque contarán como fallos, y tantos “_” como intentos restantes de los que disponga.
Tanto la palabra como las letras son arrays de char, lo que nos evita tener que andar construyendo nuevas cadenas a partir de subcadenas en cada sustitución.

La partida
Y finalmente, vamos al método partida(), que es el que desarrolla el juego. Para empezar, debo recordar que devuelve TRUE si el usuario gana la partida y FALSE si pierde. Podría hacerlo también como int porque hay juegos donde existe la posibilidad de empate, o por si quiero programar que el jugador pueda abandonar una partida sin que se contabilice como victoria o derrota.
El método debe recibir el argumento Scanner desde main(), para poder pasárselo a su vez al método leerLetra().


He estructurado el método en cuatro bloques que son:
1. Preparación de las variables
Solicito la palabra secreta al método nuevaPalabra(), creo un array de char palabra[] con la misma longitud para almacenar la letras que el jugador acierta y otro con el tamaño maxIntentos para las letras falladas vLetras[].
2. Muestro el estado de la partida y los intentos restantes, y leo una letra. Si esa letra forma parte de palabraSecreta y no de palabra[] entonces la revelo.
3. Siempre que se haya acertado una letra, se compara palabraSecreta con palabra[] (podemos convertir el contenido del array a String con el método String.copyValueOf()). Si son iguales, la palabra ha sido revelada y ponemos fin a TRUE.
Si no se ha acertado la letra, la apuntamos en vLetras y aumentamos el contador de fallos.
4. El último bloque verifica si la partida ha terminado porque la variable fin era TRUE, lo que significaría una victoria. Si hemos llegado hasta ahí y todavía vale false, es una derrota. Mostramos los mensajes adecuados y devolvemos el valor de fin.

Compilación y ejecución

El código completo
Y nada más 🙂 Espero que os haya servido para divertiros un poco programando en Java. Podéis descargaros el código fuente desde aquí y si queréis sugerir algún cambio, por favor indicádmelo en los comentarios.