En esta práctica deberá utilizar el ambiente del robot en una versión más avanzada, para ello copie, pegue en un workspace y ejecute el siguiente código:
BGSArenaWindow expertViewOn: (OnTheFlyConfigurableSimulation batteryWalkingBrush).
El mundo de robots de esta práctica ya no tiene un robot al comenzar. El mismo debe agregarse explicitamente por medio del botón etiquetado "Agregar Robot". Al agregar unrobot debe indicarelnombre con el que se lo referenciará (en este caso sugerimos utilizar la variable robotech). También es posible agregar un robot haciendo click en cualquier posición de la arena; en este caso el robot se ubicará en la posición clickeada.
Extienda el comportamiento del robot para que sea capaz de entender los siguientes mensajes. Compruebe que el robot robotech reacciona correctamente. Recuerde incluir el comentario del método en la definición de cada uno.
position: aPoint
"Posiciona al robot en la celda aPoint de la arena."
^ self body position: aPoint
squareOfSize: aSize
"Realiza un cuadrado con una esquina en su posición actual y de lado aSize."
self
brushDown;
north;
move: aSize;
east;
move: aSize;
south;
move: aSize;
west;
move: aSize;
north;
brushUp.
Alternativamente:
rotateLeft: degrees
"Gira el robot x cant de grados hacia su izquierda"
| partial_dir |
partial_dir := self direction - degrees.
partial_dir < 0
ifTrue: [ partial_dir := partial_dir + 360 ].
self direction: partial_dir
rotateRight: degrees
"Gira el robot x cant de grados hacia su derecha"
| partial_dir |
partial_dir := self direction + degrees.
partial_dir > 360
ifTrue: [ partial_dir := partial_dir - 360 ].
self direction: partial_dir
squareOfSize: aSize
"Realiza un cuadrado con una esquina en su posición actual y de lado aSize."
self brushDown.
4 timesRepeat: [
self move: aSize;
rotateRight: 90.
].
self brushUp.
squareOfSize: aSize at: aPoint
"Realiza un cuadrado con una esquina en el punto aPoint y de lado aSize."
self
position: aPoint;
squareOfSize: aSize
squareAtHomeOfSize: aSize
"Realiza un cuadrado con una esquina en 25@25 y de lado aSize."
self squareOfSize: aSize at: (25@25)
rotatedSquareOfSize: aSize
"Realiza un cuadrado con una esquina en su posición actual, de lado aSize,
rotado 45 grados."
self
rotateRight: 45;
squareOfSize: aSize.
rotatedSquareOfSize: aSize at: aPoint
"Realiza un cuadrado con una esquina en aPoint, de lado aSize, rotado 45 grados."
self
rotateRight: 45;
squareOfSize: aSize at: aPoint.
Nota: implemente los métodos reutilizando los métodos previamente definidos.
Agregue otro robot al que conocerá por medio de la variable afrodita. Compruebe que la robot afrodita entiende los mensajes definidos en el Ejercicio 1 y reacciona de igual forma que el robot robotech. ¿Qué sucede si modifica uno de los métodos? ¿Siguen comportándose ambos robots de igual manera? ¿Por qué? Discuta el por qué con un ayudante.
Una PatrolCouple (pareja de guardia) está formada por dos robots: uno conocido como patrol (patrulla) y el otro conocido como sniper (francotirador).
- Defina la clase PatrolCouple y cree una instancia en el workspace. PatrolCouple debe implementar el mensaje #patrol: sniper: que toma dos robots como parámetros y los guarda en las variables de instancia que correspondan.
Object subclass: #PatrolCouple
instanceVariableNames: 'patrol sniper'
classVariableNames: ''
category: 'BotArena'
sniper: aRobot
"Asigna un robot a sniper"
sniper := aRobot.
patrol: aRobot
"Asigna un robot a patrol"
patrol := aRobot.
patrol: aPatrol sniper: aSniper
"Asigna un patrol y un sniper"
self
patrol: aPatrol;
sniper: aSniper
sniper
"Retorna un sniper"
^ sniper
patrol
"Retorna un patrol"
^ patrol
- Cree dos robos diferentes para que sean patrol y sniper respectivamente. Cree una instancia de PatrolCouple. Por último, envíe el mensaje #patrol: sniper: con los robots como parámetros de instancia de PatrolCouple.
| patrol_couple patrol sniper|
patrol_couple := PatrolCouple new.
patrol := WalkingBrushRobot new.
sniper := WalkingBrushRobot new.
patrol_couple patrol: patrol sniper: sniper
- Según lo visto en la teoría, implemente el o los métodos necesarios para que se puedan inicializar las instancias de PatrolCouple con los robots que lo conforman por medio de un método de clase que se encargue de la "creación e inicialización".
newWithPatrol: aPatrol andSniper: aSniper
"Crea e inicializa un PatrolCouple con robots."
^ self new patrol: aPatrol sniper: aSniper
PatrolCouple con comportamiento
En este ejercicio agregaremos comportamiento a PatrolCouple con la siguiente definición de mensajes:
reset
"Ambos robots se posicionan enfrentados a distancia 5 uno del otro (uno mira al
south y el otro al north, uno de ellos posicionado en 20@20"
self patrol
north;
position: (20@30).
self sniper
south;
position: (20@25).
regularPatrol
"patrol hace un cuadrado de lado 10 rotado 45 grados alrededor de sniper,
sniper en el centro gira en sentido de las agujas del reloj."
self patrol rotatedSquareOfSize: 10 at: ((self sniper position x - 10)@(self sniper position y)).
4 timesRepeat: [self sniper rotateRight: 90]
regularPatrolTrace
"Similar a regularPatrol pero patrol realiza un trazo con el brush"
self patrol brushDown.
self regularPatrol.
self patrol brushUp.
WalkingBrushRobot >> InstanceMethods
canDoPatrol: movements
^self battery charge >= (movements * 4 + 5)
canDoRegularPatrol
^self canDoPatrol: 10
doTheRegularPatrol
"Los guardias repiten regularPatrol 5 veces, pero luego de cada una se corren
5 hacia el este. Considere usar bateria con suficiente carga"
5 timesRepeat: [
self patrol canDoRegularPatrol & (self sniper battery charge >= 9)
ifTrue: [
self patrol north.
self regularPatrol ]
]
doTheRegularPatrolTrace
"Similar a doTheRegularPatrol pero cada robot deja un trazo en la arena con
el brush"
5 timesRepeat: [
self patrol canDoRegularPatrol & (self sniper battery charge >= 9)
ifTrue: [
self patrol north.
self regularPatrol ]
]
-
Realice el diagrama de clases.
-
Implemente los métodos pedidos (no olvide agregar los comentarios a cada método). Y pruebe valiéndose del Workspace y el Inspector que funcionen correctamente.
Castle’s Watch es un antigua orden de robots que protegen un castillo.
Para lanzar el ambiente con el castillo antes debe:
-
Cerrar su simulación y workspace arena.
-
Actualizar el ambiente del robot evaluando la siguiente sentencia:
ConfigurationOfBotArena loadDevelopment
- Lanzar el ambiente con la simulación de castillo:
BGSArenaWindow expertViewOn: (CastleSimulation batteryWalkingBrush).
Defina la clase CastleWatch, la cual debe coordinar el accionar de los 4 guardianes conocidos como northWatch, southWatch, eastWatch y westWatch. Cada guardián debe cubrir el correspondiente flanco del castillo.
Object subclass: #CastleWatch
instanceVariableNames: 'northWatch southWatch eastWatch westWatch robots'
classVariableNames: ''
category: 'BotArena'
Implemente los siguientes métodos:
regularWatch
"los robots realizan un cuadrado de lado 3 en su correspondiente flanco."
self brushDown.
self robots do: [ :robot| robot squareOfSize: 3]
self brushUp
paranoicWatch
"cada robot realiza un recorrido del flanco completo, dibujando un rectangulo
de 4x10"
self brushDown.
self robots do: [ :robot | robot rectangleWithBase: 4 andHeight: 10 ].
self brushUp.
WalkingBrushRobot >> InstanceMethods
rectangleWithBase: aBase andHeight: aHeight
2 timesRepeat: [
self
move: aBase;
rotateRight: 90;
move: aHeight;
rotateRight: 90 ]
defaultPosition: aPosition
defaultPosition := aPosition
defaultPosition
^ defaultPosition
resetPosition
self position: defaultPosition
(algunos extra para que funcione lo de arriba)
initialize
robots := OrderedCollection new
robots
^ robots
brushDown
self robots do: [ :robot | robot brushDown ]
brushUp
self robots do: [ :robot | robot brushUp ]
northWatch: aRobotN southWatch: aRobotS eastWatch: aRobotE westWatch: aRobotW
self
setNorthWatch: aRobotN;
setSouthWatch: aRobotS;
setEastWatch: aRobotE;
setWestWatch: aRobotW.
eastWatch
^ eastWatch
eastWatch: aRobot
eastWatch := aRobot
northWatch
^ northWatch
northWatch: aRobot
northWatch := aRobot
westWatch
^ westWatch
westWatch: aRobot
westWatch := aRobot
southWatch
^ southWatch
southWatch: aRobot
southWatch := aRobot
setEastWatch: aRobot
self eastWatch: aRobot.
self eastWatch
defaultPosition: (36@26);
east.
robots add: eastWatch.
setNorthWatch: aRobot
self northWatch: aRobot.
self northWatch
defaultPosition: (26@16);
north.
robots add: northWatch
setSouthWatch: aRobot
self southWatch: aRobot.
self southWatch
defaultPosition: (26@36);
south.
robots add: southWatch
setWestWatch: aRobot
self westWatch: aRobot.
self westWatch
defaultPosition: (16@26);
west.
robots add: westWatch.
reset
self robots do: [ :robot| robot resetPosition ]
Note que siempre los robots deben dejar el rastro de su patrullaje. Por ej. el resultado de regularWatch podria ser:
Mientras que el resultado del paranoicWatch podría ser:
En este ejercicio abandonamos el mundo del robot y nos manejamos con el Browser del Sistema.
Debe desarrollar un post en un muro, al estilo de Facebook. Definimos un objeto Wallpost con los siguientes atributos: un texto que se desea publicar, cantidad de likes ("me gusta") y una marca que indica si es destacado o no.
Object subclass: #Wallpost
instanceVariableNames: 'likes featured text'
classVariableNames: ''
category: 'WallPost'
Defina la clase Wallpost en Smalltalk, con los siguientes mensajes:
text
"Retorna el texto descriptivo de la publicación"
^ text
text: aString
"Setea el texto descriptivo de la publicación"
text := aString
likes
"Retorna la cantidad de 'me gusta' "
^ likes
likes: aNumber
"Setter para likes"
likes := aNumber
like
"Incrementa la cantidad de likes en uno"
self likes: self likes + 1
dislike
"Decrementa la cantidad de likes en uno. Si ya es 0, no hace nada"
self likes > 0 ifTrue: [ self likes: self likes - 1 ]
featured
"getter para featured"
^ featured
featured: aValue
"setter para featured"
featured := aValue
isFeatured
"Retorna true si el post esta marcado como destacado, false en caso contrario"
^ self featured
toggleFeatured
"Cambia el post del estado destacado a no destacado y viceversa"
self featured: self featured not
initialize
"Inicializa el estado de las variables de instancia del Wallpost. Luego de
la invocación el Wallpost debe tener como texto: 'Undefined post', no debe
estar marcado como destacado y la cantidad de 'Me gusta' deben ser 0."
self
text: 'Undefined post';
featured: false;
likes: 0.
Utilice el test provisto por la cátedra para comprobar que su implementación de Wallpost es correcta.
Una vez que su implementación pasa los tests del ej anterior puede utilizar la ventana que se muestra a continuación, la cual permite inspeccionar y manipular el post (definir su texto, hacer like y dislike, marcarlo como destacado).
Para abrir la ventana puede evaluar la siguiente expresión en el workspace:
WallpostUI on: (Wallpost new)
- En la expresión:
WallpostUI on: (Wallpost new)
se instancian 2 objetos, el wallpost y la ventana. Discuta con un ayudante
- ¿En qué difieren las instanciaciones?
Una esta hecha con un constructor con parametros y la otra simplemente ejecuta el new (y si esta definido el initialize)
- En el ej. anterior ud.implementó el método #initialize, pero, ¿quien lo invoca? Ayuda: coloque un breakpoint en el método initialize para ver quién lo invoca. Ayuda de la ayuda: para poner un breakpoint agregue la sentencia self halt. al código del método #initialize.
El initialize es invocado por el new.
Leer código ayuda a conocer convenciones sobre cómo se escribe el código en Smalltalk. Utilizando el browser y mirando cualquier clase o conjunto de clases y sus métodos responda:
- ¿Los nombres de clase comienzan con minúscula o mayúscula?
En Mayuscula.
- ¿Cómo se escriben los nombres de métodos?
En minuscula.
- ¿Para qué se usan los protocolos?
Para organizar los metodos de los objetos.
- ¿Qué pasa si un método no tiene un protocolo asignado?
Absolutamente nada, solamente aparecera dentro de 'as yet unclassified'
-
Busque la clase DateAndTime y:
-
Mencione al menos 3 clases a las que se haga referencia desde el código de la clase DateAndTime.
>SecondsInDay, DosTimestamp, Duration
- Busque el método más largo de la clase. ¿Qué pasa en el browser cuando aparece un método largo?. ¿Qué conclusión puede sacar al respecto?
>'printOn: withLeadingSpace:' Aparece una senial de alerta. esta mal
>visto tener metodos demasiado largos, deberian ser modularizados
- Busque un método que haga uso de variables temporales, y dos métodos que usen de instancia. Discuta con el ayudante sobre el uso que se les da a las variables temporales.
>Las variables temporales se usan para datos que se pueden perder,
>mientras que las variables de instancia se utilizan para preservar estado
>de los objetos.
- ¿Qué devuelve Smalltalk cuando se evalúan las siguientes expresiones? Realice el ejercicio en papel, tenga en cuenta el estado del ambiente producido por las evaluaciones previas.
x := 2 * 5 factorial. "=> 240"
y := x + 1. "241"
|n| n := n+1. "=> revienta, n es nil y no entiende el mensaje +"
|n m| n := 4. m := 1. ^(n+m+x+y). "486"
5 timesRepeat: [x := x + y]. "=> 1445"
'objeto' at:2 isVowel. "revienta porq se le esta mandando el mensaje al 2 en
vez de la letra 'b'"
- Dado un triángulo rectángulo representado por las variables temporales base y altura, escriba las expresiones en Smalltalk para calcular:
Object subclass: #TrianguloRectangulo
instanceVariableNames: 'base altura'
classVariableNames: ''
category: 'Triangulos'
a. La superficie
superficie
"calcula la superficie del triangulo"
^ (self base * self altura) / 2
b. La hipotenusa
hipotenusa
"calcula la hipotenusa del triangulo"
^ (self base squared + self altura squared) sqrt
c. El perímetro
perimetro
"calcula el perimetro del triangulo"
^ self base + self altura + self hipotenusa.
d. true si el perímetro es mayor a unPerimetro o false en caso contrario
perimetro_mayor_que: unPerimetro
^ self perimetro > unPerimetro
e. Implemente en Smalltalk y verifique su correcto funcionamiento.
- Dada la clase Wallpost creada anteriormente, escriba las siguientes expresiones en Smalltalk:
a. Cree una instancia de Wallpost, unWallpost.
|unWallpost|
unWallpost := Wallpost new.
b. Incremente los likes de unWallPost hasta llegar a 20.
[ unWallpost likes < 20 ] whileTrue: [ unWallpost like ]
c. Cree otra instancia de Wallpost, otroWallpost.
otroWallpost := Wallpost new.
d. Obtener el texto del Wallpost con más likes.
unWallpost likes > otroWallpost likes
ifTrue: [ unWallpost text ]
ifFalse: [ otroWallpost text ]
e. Si el post unWallpost tiene más de 100 likes, márquelo como featured.
unWallpost likes > 100
ifTrue: [ unWallpost featured: true ].
f. Evalúe a true si ambos tienen más de 20 likes
(unWallpost likes > 20) & (otroWallpost likes > 20).
g. Cree una nueva instancia de Wallpost que sea la "concatenación" de ambos. Esto es que el texto debe ser la concatenación de los textos de ambos, sus likes deben ser la suma de ambos likes, y debe estar marcado como featured si al menos alguno de ellos lo está.
union := Wallpost new.
union text: unWallpost text,otroWallpost text.
union likes: unWallpost likes + otroWallpost likes.
union featured: unWallpost isFeatured | otroWallpost isFeatured.
- Dada una variable aNumber, escriba la expresión para calcular la suma de los primeros aNumber números naturales.
|sum|
sum:=0.
1 to: aNumber do: [:i | sum := sum + i].
- Dada la siguiente expresión:
3 + 5 > 6 ifTrue: [ 4 ] ifFalse: [ 5 ] "=> 4"
- ¿Qué valor se obtiene al ser evaluada? ¿Cómo la modificaría para obtener el valor 8?
```smalltalk
3 + (5 > 6 ifTrue: [ 4 ] ifFalse: [ 5 ]) "=> 8"
```
Considerando la clase DateAndTime, busque ejemplos de métodos donde se utilizan paréntesis para forzar cierto orden particular en la evaluación de mensajes. Discuta con un ayudante, mirando el código, qué pasaría si los paréntesis no estuvieran allí.
-
Utilizando el System Browser de Pharo, acceda a la definición de la clase Battery.
-
Viendo la definición de la clase Battery, realice un diagrama de Clases UML que la documente.
Implemente un nuevo tipo batería que se llama EnergyRecoveryCell
. Este tipo
de batería tiene el mismo comportamiento que la clase Battery pero ademas se
recarga mientras el robot se mueve. Una instancia de EnergyRecoveryCell
recarga 1 unidad de energía por cada 10 unidades de energía consumidas. Tenga
en cuenta que es indistinto si las 10 unidades se consumen en una sola movida
del robot o en varias.
Verifique su implementación utilizando el test case provisto por la cátedra
initialize
super initialize.
self steps: 0
(no usar este metodo, rompe los tests de la catedra)
canConsume: amount
"considera si puede avanzar, tiene en cuenta recargas"
^ self charge + (amount + steps // 10) >= amount
consume: amount
| full |
super consume: amount.
full := amount + self steps.
self
charge: (self charge + (full // 10));
steps: full \\ 10
steps: aNumber
"setters"
steps:= aNumber
steps
"getters"
^ steps
La idea es instanciar una EnergyRecoveryCell
especificando la cantida que va
a recargar cada 10 pasos.
Battery subclass: #EnergyRecoveryCell
instanceVariableNames: 'steps defaultCharge'
classVariableNames: ''
category: 'BotArena-Modules'.
EnergyRecoveryCell class>> newWithDefaultCharge: aCharge
^ self new defaultCharge: aCharge.
EnergyRecoveryCell>> initialize
super initialize.
self
steps: 0;
defaultCharge: 1.
EnergyRecoveryCell>> consume: amount
| full |
super consume: amount.
full := amount + self steps.
self
charge: (self charge + (full // 10 * self defaultCharge));
steps: full \\ 10.
Esto se usaria de esta manera:
|batt|
batt := EnergyRecoveryCell newWithDefaultCharge: 5.
batt charge: 100;
consume: 10;
charge. "=> 95"